Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Unified Diff: extensions/renderer/script_injection_manager.cc

Issue 321993003: Refactor renderer-side script injection (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Latest master Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/script_injection_manager.h ('k') | extensions/renderer/user_script_injection.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/renderer/script_injection_manager.cc
diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bfb0156128398269bf0a26c4448e2cdc65770295
--- /dev/null
+++ b/extensions/renderer/script_injection_manager.cc
@@ -0,0 +1,351 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/renderer/script_injection_manager.h"
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/values.h"
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_messages.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/renderer/extension_helper.h"
+#include "extensions/renderer/programmatic_script_injection.h"
+#include "extensions/renderer/script_context.h"
+#include "ipc/ipc_message_macros.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+// The length of time to wait after the DOM is complete to try and run user
+// scripts.
+const int kScriptIdleTimeoutInMs = 200;
+
+// Log information about a given script run.
+void LogScriptsRun(blink::WebFrame* frame,
+ UserScript::RunLocation location,
+ const ScriptInjection::ScriptsRunInfo& info) {
+ // Notify the browser if any extensions are now executing scripts.
+ if (!info.executing_scripts.empty()) {
+ content::RenderView* render_view =
+ content::RenderView::FromWebView(frame->view());
+ render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
+ render_view->GetRoutingID(),
+ info.executing_scripts,
+ render_view->GetPageId(),
+ ScriptContext::GetDataSourceURLForFrame(frame)));
+ }
+
+ switch (location) {
+ case UserScript::DOCUMENT_START:
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", info.num_css);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount",
+ info.num_js);
+ if (info.num_css || info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time",
+ info.timer.Elapsed());
+ break;
+ case UserScript::DOCUMENT_END:
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js);
+ if (info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed());
+ break;
+ case UserScript::DOCUMENT_IDLE:
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount",
+ info.num_js);
+ if (info.num_js)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed());
+ break;
+ case UserScript::RUN_DEFERRED:
+ // TODO(rdevlin.cronin): Add histograms.
+ break;
+ case UserScript::UNDEFINED:
+ case UserScript::RUN_LOCATION_LAST:
+ NOTREACHED();
+ }
+}
+
+} // namespace
+
+class ScriptInjectionManager::RVOHelper : public content::RenderViewObserver {
+ public:
+ RVOHelper(content::RenderView* render_view, ScriptInjectionManager* manager);
+ virtual ~RVOHelper();
+
+ private:
+ // RenderViewObserver implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void DidCreateDocumentElement(blink::WebLocalFrame* frame) OVERRIDE;
+ virtual void DidFinishDocumentLoad(blink::WebLocalFrame* frame) OVERRIDE;
+ virtual void DidFinishLoad(blink::WebLocalFrame* frame) OVERRIDE;
+ virtual void DidStartProvisionalLoad(blink::WebLocalFrame* frame) OVERRIDE;
+ virtual void FrameDetached(blink::WebFrame* frame) OVERRIDE;
+ virtual void OnDestruct() OVERRIDE;
+
+ virtual void OnExecuteCode(const ExtensionMsg_ExecuteCode_Params& params);
+ virtual void OnPermitScriptInjection(int64 request_id);
+
+ // Tells the ScriptInjectionManager to run tasks associated with
+ // document_idle.
+ void RunIdle(blink::WebFrame* frame);
+
+ ScriptInjectionManager* manager_;
+ base::WeakPtrFactory<RVOHelper> weak_factory_;
+};
+
+ScriptInjectionManager::RVOHelper::RVOHelper(
+ content::RenderView* render_view,
+ ScriptInjectionManager* manager)
+ : content::RenderViewObserver(render_view),
+ manager_(manager),
+ weak_factory_(this) {
+}
+
+ScriptInjectionManager::RVOHelper::~RVOHelper() {
+}
+
+bool ScriptInjectionManager::RVOHelper::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RVOHelper, message)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode)
+ IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection,
+ OnPermitScriptInjection)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void ScriptInjectionManager::RVOHelper::DidCreateDocumentElement(
+ blink::WebLocalFrame* frame) {
+ manager_->InjectScripts(frame, UserScript::DOCUMENT_START);
+}
+
+void ScriptInjectionManager::RVOHelper::DidFinishDocumentLoad(
+ blink::WebLocalFrame* frame) {
+ manager_->InjectScripts(frame, UserScript::DOCUMENT_END);
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ScriptInjectionManager::RVOHelper::RunIdle,
+ weak_factory_.GetWeakPtr(),
+ frame),
+ base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs));
+}
+
+void ScriptInjectionManager::RVOHelper::DidFinishLoad(
+ blink::WebLocalFrame* frame) {
+ // Ensure that running scripts does not keep any progress UI running.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ScriptInjectionManager::RVOHelper::RunIdle,
+ weak_factory_.GetWeakPtr(),
+ frame));
+}
+
+void ScriptInjectionManager::RVOHelper::DidStartProvisionalLoad(
+ blink::WebLocalFrame* frame) {
+ manager_->InvalidateForFrame(frame);
+}
+
+void ScriptInjectionManager::RVOHelper::FrameDetached(blink::WebFrame* frame) {
+ manager_->InvalidateForFrame(frame);
+}
+
+void ScriptInjectionManager::RVOHelper::OnDestruct() {
+ manager_->RemoveObserver(this);
+}
+
+void ScriptInjectionManager::RVOHelper::OnExecuteCode(
+ const ExtensionMsg_ExecuteCode_Params& params) {
+ manager_->HandleExecuteCode(params, render_view());
+}
+
+void ScriptInjectionManager::RVOHelper::OnPermitScriptInjection(
+ int64 request_id) {
+ manager_->HandlePermitScriptInjection(request_id);
+}
+
+void ScriptInjectionManager::RVOHelper::RunIdle(blink::WebFrame* frame) {
+ manager_->InjectScripts(frame, UserScript::DOCUMENT_IDLE);
+}
+
+ScriptInjectionManager::ScriptInjectionManager(
+ const ExtensionSet* extensions,
+ UserScriptSet* user_script_set)
+ : extensions_(extensions),
+ user_script_set_(user_script_set),
+ user_script_set_observer_(this) {
+ user_script_set_observer_.Add(user_script_set_);
+}
+
+ScriptInjectionManager::~ScriptInjectionManager() {
+}
+
+void ScriptInjectionManager::OnRenderViewCreated(
+ content::RenderView* render_view) {
+ rvo_helpers_.push_back(new RVOHelper(render_view, this));
+}
+
+void ScriptInjectionManager::OnUserScriptsUpdated(
+ const std::set<std::string>& changed_extensions,
+ const std::vector<UserScript*>& scripts) {
+ for (ScopedVector<ScriptInjection>::iterator iter =
+ pending_injections_.begin();
+ iter != pending_injections_.end();) {
+ if (changed_extensions.count((*iter)->extension_id()) > 0)
+ iter = pending_injections_.erase(iter);
+ else
+ ++iter;
+ }
+}
+
+void ScriptInjectionManager::RemoveObserver(RVOHelper* helper) {
+ for (ScopedVector<RVOHelper>::iterator iter = rvo_helpers_.begin();
+ iter != rvo_helpers_.end();
+ ++iter) {
+ if (*iter == helper) {
+ rvo_helpers_.erase(iter);
+ break;
+ }
+ }
+}
+
+void ScriptInjectionManager::InvalidateForFrame(blink::WebFrame* frame) {
+ for (ScopedVector<ScriptInjection>::iterator iter =
+ pending_injections_.begin();
+ iter != pending_injections_.end();) {
+ if ((*iter)->web_frame() == frame)
+ iter = pending_injections_.erase(iter);
+ else
+ ++iter;
+ }
+
+ frame_statuses_.erase(frame);
+}
+
+void ScriptInjectionManager::InjectScripts(
+ blink::WebFrame* frame, UserScript::RunLocation run_location) {
+ FrameStatusMap::iterator iter = frame_statuses_.find(frame);
+ // Check if the frame is already in our map.
+ if (iter == frame_statuses_.end()) {
+ // If the frame isn't in our map, and the run location isn't document_start,
+ // then abort. This can happen in two ways:
+ // 1. We just received a delayed idle run for a frame which is invalidated.
+ // Obviously, we don't want to run.
+ // 2. We somehow received a document_end or document_idle notification
+ // without ever receiving a document_start. We don't want to run because
+ // extensions may have requirements that scripts running at start have
+ // run by the time scripts run at idle. Better to just not run.
+ if (run_location != UserScript::DOCUMENT_START)
+ return;
+
+ // Otherwise, add a new entry to the map.
+ frame_statuses_[frame] = UserScript::DOCUMENT_START;
+ } else { // Already in the map.
+ // If we've already run the given location (happens in the case of idle
+ // since we notify in two places), return.
+ if (iter->second == run_location)
+ return;
+
+ // Otherwise, update the frame status.
+ iter->second = run_location;
+ }
+
+ // Inject any scripts that were waiting for the right run location.
+ ScriptInjection::ScriptsRunInfo scripts_run_info;
+ for (ScopedVector<ScriptInjection>::iterator iter =
+ pending_injections_.begin();
+ iter != pending_injections_.end();) {
+ if ((*iter)->web_frame() == frame &&
+ (*iter)->TryToInject(run_location,
+ extensions_->GetByID((*iter)->extension_id()),
+ &scripts_run_info)) {
+ iter = pending_injections_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ // Try to inject any user scripts that should run for this location. If they
+ // don't complete their injection (for example, waiting for a permission
+ // response) then they will be added to |pending_injections_|.
+ ScopedVector<ScriptInjection> user_script_injections;
+ int tab_id = ExtensionHelper::Get(content::RenderView::FromWebView(
+ frame->top()->view()))->tab_id();
+ user_script_set_->GetInjections(
+ &user_script_injections, frame, tab_id, run_location);
+ for (ScopedVector<ScriptInjection>::iterator iter =
+ user_script_injections.begin();
+ iter != user_script_injections.end();) {
+ scoped_ptr<ScriptInjection> injection(*iter);
+ iter = user_script_injections.weak_erase(iter);
+ if (!injection->TryToInject(run_location,
+ extensions_->GetByID(injection->extension_id()),
+ &scripts_run_info)) {
+ pending_injections_.push_back(injection.release());
+ }
+ }
+
+ LogScriptsRun(frame, run_location, scripts_run_info);
+}
+
+void ScriptInjectionManager::HandleExecuteCode(
+ const ExtensionMsg_ExecuteCode_Params& params,
+ content::RenderView* render_view) {
+ blink::WebFrame* main_frame = render_view->GetWebView()->mainFrame();
+ if (!main_frame) {
+ render_view->Send(
+ new ExtensionHostMsg_ExecuteCodeFinished(render_view->GetRoutingID(),
+ params.request_id,
+ "No main frame",
+ -1,
+ GURL(std::string()),
+ base::ListValue()));
+ return;
+ }
+
+ scoped_ptr<ScriptInjection> injection(new ProgrammaticScriptInjection(
+ main_frame, params, ExtensionHelper::Get(render_view)->tab_id()));
+
+ ScriptInjection::ScriptsRunInfo scripts_run_info;
+ FrameStatusMap::const_iterator iter = frame_statuses_.find(main_frame);
+ if (!injection->TryToInject(
+ iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second,
+ extensions_->GetByID(injection->extension_id()),
+ &scripts_run_info)) {
+ pending_injections_.push_back(injection.release());
+ }
+}
+
+void ScriptInjectionManager::HandlePermitScriptInjection(int request_id) {
+ ScopedVector<ScriptInjection>::iterator iter =
+ pending_injections_.begin();
+ for (; iter != pending_injections_.end(); ++iter) {
+ if ((*iter)->request_id() == request_id)
+ break;
+ }
+ if (iter == pending_injections_.end())
+ return;
+
+ scoped_ptr<ScriptInjection> injection(*iter);
+ pending_injections_.weak_erase(iter);
+
+ ScriptInjection::ScriptsRunInfo scripts_run_info;
+ if (injection->OnPermissionGranted(extensions_->GetByID(
+ injection->extension_id()),
+ &scripts_run_info)) {
+ LogScriptsRun(
+ injection->web_frame(), UserScript::RUN_DEFERRED, scripts_run_info);
+ }
+}
+
+} // namespace extensions
« no previous file with comments | « extensions/renderer/script_injection_manager.h ('k') | extensions/renderer/user_script_injection.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698