| 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();
|
| }
|
|
|