A refacotring in the renderer side: introduce InjectionHost to de-couple extensions and to support content script injection out of chrome extensions.
BUG=437566
Review URL: https://codereview.chromium.org/885493007
Cr-Commit-Position: refs/heads/master@{#316283}
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index 9f3236f..94affa0 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -12,12 +12,13 @@
#include "base/values.h"
#include "content/public/child/v8_value_converter.h"
#include "content/public/renderer/render_view.h"
-#include "extensions/common/extension.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/feature_switch.h"
+#include "extensions/common/host_id.h"
#include "extensions/common/manifest_handlers/csp_info.h"
#include "extensions/renderer/dom_activity_logger.h"
#include "extensions/renderer/extension_groups.h"
+#include "extensions/renderer/extension_injection_host.h"
#include "extensions/renderer/extensions_renderer_client.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebDocument.h"
@@ -31,7 +32,7 @@
namespace {
-typedef std::map<std::string, int> IsolatedWorldMap;
+using IsolatedWorldMap = std::map<std::string, int>;
base::LazyInstance<IsolatedWorldMap> g_isolated_worlds =
LAZY_INSTANCE_INITIALIZER;
@@ -55,38 +56,38 @@
}
}
-// Gets the isolated world ID to use for the given |extension| in the given
-// |frame|. If no isolated world has been created for that extension,
-// one will be created and initialized.
-int GetIsolatedWorldIdForExtension(const Extension* extension,
- blink::WebLocalFrame* frame) {
+// Gets the isolated world ID to use for the given |injection_host|
+// in the given |frame|. If no isolated world has been created for that
+// |injection_host| one will be created and initialized.
+int GetIsolatedWorldIdForInstance(const InjectionHost* injection_host,
+ blink::WebLocalFrame* frame) {
static int g_next_isolated_world_id =
ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId();
IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
int id = 0;
- IsolatedWorldMap::iterator iter = isolated_worlds.find(extension->id());
+ const std::string& key = injection_host->id().id();
+ IsolatedWorldMap::iterator iter = isolated_worlds.find(key);
if (iter != isolated_worlds.end()) {
id = iter->second;
} else {
id = g_next_isolated_world_id++;
// This map will tend to pile up over time, but realistically, you're never
- // going to have enough extensions for it to matter.
- isolated_worlds[extension->id()] = id;
+ // going to have enough injection hosts for it to matter.
+ isolated_worlds[key] = id;
}
// We need to set the isolated world origin and CSP even if it's not a new
// world since these are stored per frame, and we might not have used this
// isolated world in this frame before.
frame->setIsolatedWorldSecurityOrigin(
- id, blink::WebSecurityOrigin::create(extension->url()));
+ id, blink::WebSecurityOrigin::create(injection_host->url()));
frame->setIsolatedWorldContentSecurityPolicy(
- id,
- blink::WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
+ id, blink::WebString::fromUTF8(
+ injection_host->GetContentSecurityPolicy()));
frame->setIsolatedWorldHumanReadableName(
- id,
- blink::WebString::fromUTF8(extension->name()));
+ id, blink::WebString::fromUTF8(injection_host->name()));
return id;
}
@@ -94,33 +95,30 @@
} // namespace
// static
-std::string ScriptInjection::GetExtensionIdForIsolatedWorld(
- int isolated_world_id) {
- IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
+std::string ScriptInjection::GetHostIdForIsolatedWorld(int isolated_world_id) {
+ const IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get();
- for (IsolatedWorldMap::iterator iter = isolated_worlds.begin();
- iter != isolated_worlds.end();
- ++iter) {
- if (iter->second == isolated_world_id)
- return iter->first;
+ for (const auto& iter : isolated_worlds) {
+ if (iter.second == isolated_world_id)
+ return iter.first;
}
return std::string();
}
// static
-void ScriptInjection::RemoveIsolatedWorld(const std::string& extension_id) {
- g_isolated_worlds.Get().erase(extension_id);
+void ScriptInjection::RemoveIsolatedWorld(const std::string& host_id) {
+ g_isolated_worlds.Get().erase(host_id);
}
ScriptInjection::ScriptInjection(
scoped_ptr<ScriptInjector> injector,
blink::WebLocalFrame* web_frame,
- const std::string& extension_id,
+ const HostID& host_id,
UserScript::RunLocation run_location,
int tab_id)
: injector_(injector.Pass()),
web_frame_(web_frame),
- extension_id_(extension_id),
+ host_id_(host_id),
run_location_(run_location),
tab_id_(tab_id),
request_id_(kInvalidRequestId),
@@ -133,7 +131,7 @@
}
bool ScriptInjection::TryToInject(UserScript::RunLocation current_location,
- const Extension* extension,
+ const InjectionHost* injection_host,
ScriptsRunInfo* scripts_run_info) {
if (current_location < run_location_)
return false; // Wait for the right location.
@@ -141,13 +139,13 @@
if (request_id_ != kInvalidRequestId)
return false; // We're waiting for permission right now, try again later.
- if (!extension) {
+ if (!injection_host) {
NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
return true; // We're done.
}
- switch (injector_->CanExecuteOnFrame(
- extension, web_frame_, tab_id_, web_frame_->top()->document().url())) {
+ switch (injector_->CanExecuteOnFrame(injection_host, web_frame_, tab_id_,
+ web_frame_->top()->document().url())) {
case PermissionsData::ACCESS_DENIED:
NotifyWillNotInject(ScriptInjector::NOT_ALLOWED);
return true; // We're done.
@@ -155,7 +153,7 @@
RequestPermission();
return false; // Wait around for permission.
case PermissionsData::ACCESS_ALLOWED:
- Inject(extension, scripts_run_info);
+ Inject(injection_host, scripts_run_info);
return true; // We're done!
}
@@ -164,14 +162,14 @@
return false;
}
-bool ScriptInjection::OnPermissionGranted(const Extension* extension,
+bool ScriptInjection::OnPermissionGranted(const InjectionHost* injection_host,
ScriptsRunInfo* scripts_run_info) {
- if (!extension) {
+ if (!injection_host) {
NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED);
return false;
}
- Inject(extension, scripts_run_info);
+ Inject(injection_host, scripts_run_info);
return true;
}
@@ -185,7 +183,7 @@
: g_next_pending_id++;
render_view->Send(new ExtensionHostMsg_RequestScriptInjectionPermission(
render_view->GetRoutingID(),
- extension_id_,
+ host_id_.id(),
injector_->script_type(),
request_id_));
}
@@ -196,13 +194,14 @@
injector_->OnWillNotInject(reason);
}
-void ScriptInjection::Inject(const Extension* extension,
+void ScriptInjection::Inject(const InjectionHost* injection_host,
ScriptsRunInfo* scripts_run_info) {
- DCHECK(extension);
+ DCHECK(injection_host);
DCHECK(scripts_run_info);
DCHECK(!complete_);
- if (ShouldNotifyBrowserOfInjections())
+ if (ShouldNotifyBrowserOfInjections() &&
+ injection_host->id().type() == HostID::EXTENSIONS)
RequestPermission();
std::vector<blink::WebFrame*> frame_vector;
@@ -229,30 +228,32 @@
// We recheck access here in the renderer for extra safety against races
// with navigation, but different frames can have different URLs, and the
- // extension might only have access to a subset of them.
- // For child frames, we just skip ones the extension doesn't have access
- // to and carry on.
+ // injection host might only have access to a subset of them.
+ // For child frames, we just skip ones the injection host doesn't have
+ // access to and carry on.
// Note: we don't consider ACCESS_WITHHELD because there is nowhere to
// surface a request for a child frame.
// TODO(rdevlin.cronin): We should ask for permission somehow.
- if (injector_->CanExecuteOnFrame(extension, frame, tab_id_, top_url) ==
+ if (injector_->CanExecuteOnFrame(injection_host, frame, tab_id_, top_url) ==
PermissionsData::ACCESS_DENIED) {
DCHECK(frame->parent());
continue;
}
if (inject_js)
- InjectJs(extension, frame, execution_results.get());
+ InjectJs(injection_host, frame, execution_results.get());
if (inject_css)
InjectCss(frame);
}
complete_ = true;
+
+ // TODO(hanxi): don't log these metrics for webUIs' injections.
injector_->OnInjectionComplete(execution_results.Pass(),
scripts_run_info,
run_location_);
}
-void ScriptInjection::InjectJs(const Extension* extension,
+void ScriptInjection::InjectJs(const InjectionHost* injection_host,
blink::WebLocalFrame* frame,
base::ListValue* execution_results) {
std::vector<blink::WebScriptSource> sources =
@@ -260,11 +261,12 @@
bool in_main_world = injector_->ShouldExecuteInMainWorld();
int world_id = in_main_world
? DOMActivityLogger::kMainWorldId
- : GetIsolatedWorldIdForExtension(extension, frame);
+ : GetIsolatedWorldIdForInstance(injection_host, frame);
bool expects_results = injector_->ExpectsResults();
base::ElapsedTimer exec_timer;
- DOMActivityLogger::AttachToWorld(world_id, extension->id());
+ if (injection_host->id().type() == HostID::EXTENSIONS)
+ DOMActivityLogger::AttachToWorld(world_id, injection_host->id().id());
v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Local<v8::Value> script_value;
if (in_main_world) {
@@ -289,7 +291,8 @@
script_value = (*results)[0];
}
- UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
+ if (injection_host->id().type() == HostID::EXTENSIONS)
+ UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed());
if (expects_results) {
// Right now, we only support returning single results (per frame).