Index: extensions/renderer/user_script_slave.cc |
diff --git a/extensions/renderer/user_script_slave.cc b/extensions/renderer/user_script_slave.cc |
index 8e176effe205d75e42ad0e988e02f405dfc450f6..036581b3cd5502a929b4014cf0517353a3c8c8dc 100644 |
--- a/extensions/renderer/user_script_slave.cc |
+++ b/extensions/renderer/user_script_slave.cc |
@@ -6,14 +6,11 @@ |
#include <map> |
-#include "base/command_line.h" |
#include "base/logging.h" |
#include "base/memory/shared_memory.h" |
#include "base/metrics/histogram.h" |
#include "base/pickle.h" |
-#include "base/strings/stringprintf.h" |
#include "base/timer/elapsed_timer.h" |
-#include "content/public/common/url_constants.h" |
#include "content/public/renderer/render_thread.h" |
#include "content/public/renderer/render_view.h" |
#include "extensions/common/extension.h" |
@@ -21,66 +18,48 @@ |
#include "extensions/common/extension_set.h" |
#include "extensions/common/manifest_handlers/csp_info.h" |
#include "extensions/common/permissions/permissions_data.h" |
-#include "extensions/renderer/dom_activity_logger.h" |
-#include "extensions/renderer/extension_groups.h" |
#include "extensions/renderer/extensions_renderer_client.h" |
#include "extensions/renderer/script_context.h" |
-#include "grit/renderer_resources.h" |
-#include "third_party/WebKit/public/platform/WebURLRequest.h" |
-#include "third_party/WebKit/public/platform/WebVector.h" |
-#include "third_party/WebKit/public/web/WebDocument.h" |
#include "third_party/WebKit/public/web/WebFrame.h" |
#include "third_party/WebKit/public/web/WebSecurityOrigin.h" |
#include "third_party/WebKit/public/web/WebSecurityPolicy.h" |
#include "third_party/WebKit/public/web/WebView.h" |
-#include "ui/base/resource/resource_bundle.h" |
#include "url/gurl.h" |
using blink::WebFrame; |
using blink::WebSecurityOrigin; |
using blink::WebSecurityPolicy; |
using blink::WebString; |
-using blink::WebVector; |
using blink::WebView; |
using content::RenderThread; |
namespace extensions { |
-// These two strings are injected before and after the Greasemonkey API and |
-// user script to wrap it in an anonymous scope. |
-static const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; |
-static const char kUserScriptTail[] = "\n})(window);"; |
- |
int UserScriptSlave::GetIsolatedWorldIdForExtension(const Extension* extension, |
WebFrame* frame) { |
static int g_next_isolated_world_id = |
ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); |
+ int id = 0; |
IsolatedWorldMap::iterator iter = isolated_world_ids_.find(extension->id()); |
if (iter != isolated_world_ids_.end()) { |
- // 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( |
- iter->second, WebSecurityOrigin::create(extension->url())); |
- frame->setIsolatedWorldContentSecurityPolicy( |
- iter->second, |
- WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension))); |
- return iter->second; |
+ 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_world_ids_[extension->id()] = id; |
} |
- int new_id = g_next_isolated_world_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_world_ids_[extension->id()] = new_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( |
- new_id, WebSecurityOrigin::create(extension->url())); |
+ id, WebSecurityOrigin::create(extension->url())); |
frame->setIsolatedWorldContentSecurityPolicy( |
- new_id, |
- WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension))); |
- return new_id; |
+ id, WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension))); |
+ |
+ return id; |
} |
std::string UserScriptSlave::GetExtensionIdForIsolatedWorld( |
@@ -99,9 +78,7 @@ void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) { |
} |
UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions) |
- : script_deleter_(&scripts_), extensions_(extensions) { |
- api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource( |
- IDR_GREASEMONKEY_API_JS); |
+ : extensions_(extensions) { |
} |
UserScriptSlave::~UserScriptSlave() { |
@@ -109,14 +86,23 @@ UserScriptSlave::~UserScriptSlave() { |
void UserScriptSlave::GetActiveExtensions( |
std::set<std::string>* extension_ids) { |
- for (size_t i = 0; i < scripts_.size(); ++i) { |
- DCHECK(!scripts_[i]->extension_id().empty()); |
- extension_ids->insert(scripts_[i]->extension_id()); |
+ DCHECK(extension_ids); |
+ for (ScopedVector<ScriptInjection>::const_iterator iter = |
+ script_injections_.begin(); |
+ iter != script_injections_.end(); |
+ ++iter) { |
+ DCHECK(!(*iter)->extension_id().empty()); |
+ extension_ids->insert((*iter)->extension_id()); |
} |
} |
+const Extension* UserScriptSlave::GetExtension( |
+ const std::string& extension_id) { |
+ return extensions_->GetByID(extension_id); |
+} |
+ |
bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { |
- scripts_.clear(); |
+ script_injections_.clear(); |
bool only_inject_incognito = |
ExtensionsRendererClient::Get()->IsIncognitoProcess(); |
@@ -144,10 +130,9 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { |
PickleIterator iter(pickle); |
CHECK(pickle.ReadUInt64(&iter, &num_scripts)); |
- scripts_.reserve(num_scripts); |
+ script_injections_.reserve(num_scripts); |
for (uint64 i = 0; i < num_scripts; ++i) { |
- scripts_.push_back(new UserScript()); |
- UserScript* script = scripts_.back(); |
+ scoped_ptr<UserScript> script(new UserScript()); |
script->Unpickle(pickle, &iter); |
// Note that this is a pointer into shared memory. We don't own it. It gets |
@@ -168,11 +153,10 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { |
base::StringPiece(body, body_length)); |
} |
- if (only_inject_incognito && !script->is_incognito_enabled()) { |
- // This script shouldn't run in an incognito tab. |
- delete script; |
- scripts_.pop_back(); |
- } |
+ if (only_inject_incognito && !script->is_incognito_enabled()) |
+ continue; // This script shouldn't run in an incognito tab. |
+ |
+ script_injections_.push_back(new ScriptInjection(script.Pass(), this)); |
} |
return true; |
@@ -180,145 +164,70 @@ bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) { |
void UserScriptSlave::InjectScripts(WebFrame* frame, |
UserScript::RunLocation location) { |
- GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); |
- if (data_source_url.is_empty()) |
+ GURL document_url = ScriptInjection::GetDocumentUrlForFrame(frame); |
+ if (document_url.is_empty()) |
return; |
- if (frame->isViewSourceModeEnabled()) |
- data_source_url = GURL(content::kViewSourceScheme + std::string(":") + |
- data_source_url.spec()); |
- |
- base::ElapsedTimer timer; |
- int num_css = 0; |
- int num_scripts = 0; |
- |
- ExecutingScriptsMap extensions_executing_scripts; |
- |
- blink::WebFrame* top_frame = frame->top(); |
content::RenderView* top_render_view = |
- content::RenderView::FromWebView(top_frame->view()); |
+ content::RenderView::FromWebView(frame->top()->view()); |
- for (size_t i = 0; i < scripts_.size(); ++i) { |
- std::vector<WebScriptSource> sources; |
- UserScript* script = scripts_[i]; |
- |
- if (frame->parent() && !script->match_all_frames()) |
- continue; // Only match subframes if the script declared it wanted to. |
- |
- const Extension* extension = extensions_->GetByID(script->extension_id()); |
- |
- // Since extension info is sent separately from user script info, they can |
- // be out of sync. We just ignore this situation. |
- if (!extension) |
+ ScriptInjection::ScriptsRunInfo scripts_run_info; |
+ for (ScopedVector<ScriptInjection>::const_iterator iter = |
+ script_injections_.begin(); |
+ iter != script_injections_.end(); |
+ ++iter) { |
+ ScriptInjection* injection = *iter; |
+ if (!injection->WantsToRun(frame, location, document_url)) |
continue; |
- const GURL& document_url = ScriptContext::GetEffectiveDocumentURL( |
- frame, data_source_url, script->match_about_blank()); |
- |
- // Content scripts are not tab-specific. |
- const int kNoTabId = -1; |
- // We don't have a process id in this context. |
- const int kNoProcessId = -1; |
- if (!PermissionsData::CanExecuteScriptOnPage(extension, |
- document_url, |
- top_frame->document().url(), |
- kNoTabId, |
- script, |
- kNoProcessId, |
- NULL)) { |
- continue; |
- } |
+ const Extension* extension = GetExtension(injection->extension_id()); |
+ DCHECK(extension); |
- if (location == UserScript::DOCUMENT_START) { |
- num_css += script->css_scripts().size(); |
- for (UserScript::FileList::const_iterator iter = |
- script->css_scripts().begin(); |
- iter != script->css_scripts().end(); |
- ++iter) { |
- frame->document().insertStyleSheet( |
- WebString::fromUTF8(iter->GetContent().as_string())); |
- } |
- } |
- |
- if (script->run_location() == location) { |
+ if (PermissionsData::RequiresActionForScriptExecution(extension)) { |
// TODO(rdevlin.cronin): Right now, this is just a notification, but soon |
// we should block without user consent. |
- if (PermissionsData::RequiresActionForScriptExecution(extension)) { |
- top_render_view->Send( |
- new ExtensionHostMsg_NotifyExtensionScriptExecution( |
- top_render_view->GetRoutingID(), |
- extension->id(), |
- top_render_view->GetPageId())); |
- } |
- num_scripts += script->js_scripts().size(); |
- for (size_t j = 0; j < script->js_scripts().size(); ++j) { |
- UserScript::File& file = script->js_scripts()[j]; |
- std::string content = file.GetContent().as_string(); |
- |
- // We add this dumb function wrapper for standalone user script to |
- // emulate what Greasemonkey does. |
- // TODO(aa): I think that maybe "is_standalone" scripts don't exist |
- // anymore. Investigate. |
- if (script->is_standalone() || script->emulate_greasemonkey()) { |
- content.insert(0, kUserScriptHead); |
- content += kUserScriptTail; |
- } |
- sources.push_back( |
- WebScriptSource(WebString::fromUTF8(content), file.url())); |
- } |
+ top_render_view->Send( |
+ new ExtensionHostMsg_NotifyExtensionScriptExecution( |
+ top_render_view->GetRoutingID(), |
+ extension->id(), |
+ top_render_view->GetPageId())); |
} |
- if (!sources.empty()) { |
- // Emulate Greasemonkey API for scripts that were converted to extensions |
- // and "standalone" user scripts. |
- if (script->is_standalone() || script->emulate_greasemonkey()) { |
- sources.insert( |
- sources.begin(), |
- WebScriptSource(WebString::fromUTF8(api_js_.as_string()))); |
- } |
- |
- int isolated_world_id = GetIsolatedWorldIdForExtension(extension, frame); |
- |
- base::ElapsedTimer exec_timer; |
- DOMActivityLogger::AttachToWorld(isolated_world_id, extension->id()); |
- frame->executeScriptInIsolatedWorld(isolated_world_id, |
- &sources.front(), |
- sources.size(), |
- EXTENSION_GROUP_CONTENT_SCRIPTS); |
- UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed()); |
- |
- for (std::vector<WebScriptSource>::const_iterator iter = sources.begin(); |
- iter != sources.end(); |
- ++iter) { |
- extensions_executing_scripts[extension->id()].insert( |
- GURL(iter->url).path()); |
- } |
- } |
+ injection->Inject(frame, location, &scripts_run_info); |
} |
+ LogScriptsRun(frame, location, scripts_run_info); |
+} |
+ |
+void UserScriptSlave::LogScriptsRun( |
+ blink::WebFrame* frame, |
+ UserScript::RunLocation location, |
+ const ScriptInjection::ScriptsRunInfo& info) { |
// Notify the browser if any extensions are now executing scripts. |
- if (!extensions_executing_scripts.empty()) { |
- top_render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting( |
- top_render_view->GetRoutingID(), |
- extensions_executing_scripts, |
- top_render_view->GetPageId(), |
- ScriptContext::GetDataSourceURLForFrame(top_frame))); |
+ 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))); |
} |
- // Log debug info. |
if (location == UserScript::DOCUMENT_START) { |
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_CssCount", num_css); |
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectStart_ScriptCount", num_scripts); |
- if (num_css || num_scripts) |
- UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", timer.Elapsed()); |
+ 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()); |
} else if (location == UserScript::DOCUMENT_END) { |
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", num_scripts); |
- if (num_scripts) |
- UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", timer.Elapsed()); |
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectEnd_ScriptCount", info.num_js); |
+ if (info.num_js) |
+ UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", info.timer.Elapsed()); |
} else if (location == UserScript::DOCUMENT_IDLE) { |
- UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", num_scripts); |
- if (num_scripts) |
- UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", timer.Elapsed()); |
+ UMA_HISTOGRAM_COUNTS_100("Extensions.InjectIdle_ScriptCount", info.num_js); |
+ if (info.num_js) |
+ UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", info.timer.Elapsed()); |
} else { |
NOTREACHED(); |
} |