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

Unified Diff: chrome/renderer/extensions/user_script_slave.cc

Issue 112203003: Fix renderer crashes when frame gets detached while injectng user scripts. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Defer frame destruction until event loop. Created 6 years, 10 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
Index: chrome/renderer/extensions/user_script_slave.cc
diff --git a/chrome/renderer/extensions/user_script_slave.cc b/chrome/renderer/extensions/user_script_slave.cc
index fc538910c016c271713d6f8d69bbae08c28eaf9d..cfd952540117a7a32744265632f7703a60e93736 100644
--- a/chrome/renderer/extensions/user_script_slave.cc
+++ b/chrome/renderer/extensions/user_script_slave.cc
@@ -1,381 +1,383 @@
-// Copyright (c) 2012 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 "chrome/renderer/extensions/user_script_slave.h"
-
-#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 "chrome/common/extensions/extension_messages.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/renderer/chrome_render_process_observer.h"
-#include "chrome/renderer/extensions/dom_activity_logger.h"
-#include "chrome/renderer/extensions/extension_groups.h"
-#include "chrome/renderer/isolated_world_ids.h"
-#include "content/public/renderer/render_thread.h"
-#include "content/public/renderer/render_view.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_set.h"
-#include "extensions/common/manifest_handlers/csp_info.h"
-#include "extensions/common/permissions/permissions_data.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/WebDataSource.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 = chrome::ISOLATED_WORLD_ID_EXTENSIONS;
-
- 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;
- }
-
- 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;
- InitializeIsolatedWorld(new_id, extension);
- frame->setIsolatedWorldSecurityOrigin(
- new_id,
- WebSecurityOrigin::create(extension->url()));
- frame->setIsolatedWorldContentSecurityPolicy(
- new_id,
- WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
- return new_id;
-}
-
-std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
- int isolated_world_id) {
- for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
- iter != isolated_world_ids_.end(); ++iter) {
- if (iter->second == isolated_world_id)
- return iter->first;
- }
- return std::string();
-}
-
-// static
-void UserScriptSlave::InitializeIsolatedWorld(int isolated_world_id,
- const Extension* extension) {
- const URLPatternSet& permissions =
- PermissionsData::GetEffectiveHostPermissions(extension);
- for (URLPatternSet::const_iterator i = permissions.begin();
- i != permissions.end(); ++i) {
- const char* schemes[] = {
- content::kHttpScheme,
- content::kHttpsScheme,
- content::kFileScheme,
- content::kChromeUIScheme,
- };
- for (size_t j = 0; j < arraysize(schemes); ++j) {
- if (i->MatchesScheme(schemes[j])) {
- WebSecurityPolicy::addOriginAccessWhitelistEntry(
- extension->url(),
- WebString::fromUTF8(schemes[j]),
- WebString::fromUTF8(i->host()),
- i->match_subdomains());
- }
- }
- }
-}
-
-void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
- isolated_world_ids_.erase(extension_id);
-}
-
-UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
- : script_deleter_(&scripts_), extensions_(extensions) {
- api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_GREASEMONKEY_API_JS);
-}
-
-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());
- }
-}
-
-bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
- scripts_.clear();
-
- bool only_inject_incognito =
- ChromeRenderProcessObserver::is_incognito_process();
-
- // Create the shared memory object (read only).
- shared_memory_.reset(new base::SharedMemory(shared_memory, true));
- if (!shared_memory_.get())
- return false;
-
- // First get the size of the memory block.
- if (!shared_memory_->Map(sizeof(Pickle::Header)))
- return false;
- Pickle::Header* pickle_header =
- reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
-
- // Now map in the rest of the block.
- int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
- shared_memory_->Unmap();
- if (!shared_memory_->Map(pickle_size))
- return false;
-
- // Unpickle scripts.
- uint64 num_scripts = 0;
- Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
- pickle_size);
- PickleIterator iter(pickle);
- CHECK(pickle.ReadUInt64(&iter, &num_scripts));
-
- scripts_.reserve(num_scripts);
- for (uint64 i = 0; i < num_scripts; ++i) {
- scripts_.push_back(new UserScript());
- UserScript* script = scripts_.back();
- script->Unpickle(pickle, &iter);
-
- // Note that this is a pointer into shared memory. We don't own it. It gets
- // cleared up when the last renderer or browser process drops their
- // reference to the shared memory.
- for (size_t j = 0; j < script->js_scripts().size(); ++j) {
- const char* body = NULL;
- int body_length = 0;
- CHECK(pickle.ReadData(&iter, &body, &body_length));
- script->js_scripts()[j].set_external_content(
- base::StringPiece(body, body_length));
- }
- for (size_t j = 0; j < script->css_scripts().size(); ++j) {
- const char* body = NULL;
- int body_length = 0;
- CHECK(pickle.ReadData(&iter, &body, &body_length));
- script->css_scripts()[j].set_external_content(
- 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();
- }
- }
-
- // Push user styles down into WebCore
- RenderThread::Get()->EnsureWebKitInitialized();
- WebView::removeInjectedStyleSheets();
- for (size_t i = 0; i < scripts_.size(); ++i) {
- UserScript* script = scripts_[i];
- if (script->css_scripts().empty())
- continue;
-
- WebVector<WebString> patterns;
- std::vector<WebString> temp_patterns;
- const URLPatternSet& url_patterns = script->url_patterns();
- for (URLPatternSet::const_iterator k = url_patterns.begin();
- k != url_patterns.end(); ++k) {
- URLPatternList explicit_patterns = k->ConvertToExplicitSchemes();
- for (size_t m = 0; m < explicit_patterns.size(); ++m) {
- temp_patterns.push_back(WebString::fromUTF8(
- explicit_patterns[m].GetAsString()));
- }
- }
- patterns.assign(temp_patterns);
-
- for (size_t j = 0; j < script->css_scripts().size(); ++j) {
- const UserScript::File& file = scripts_[i]->css_scripts()[j];
- std::string content = file.GetContent().as_string();
-
- WebView::injectStyleSheet(
- WebString::fromUTF8(content),
- patterns,
- script->match_all_frames() ?
- WebView::InjectStyleInAllFrames :
- WebView::InjectStyleInTopFrameOnly);
- }
- }
-
- return true;
-}
-
-GURL UserScriptSlave::GetDataSourceURLForFrame(const WebFrame* frame) {
- // Normally we would use frame->document().url() to determine the document's
- // URL, but to decide whether to inject a content script, we use the URL from
- // the data source. This "quirk" helps prevents content scripts from
- // inadvertently adding DOM elements to the compose iframe in Gmail because
- // the compose iframe's dataSource URL is about:blank, but the document URL
- // changes to match the parent document after Gmail document.writes into
- // it to create the editor.
- // http://code.google.com/p/chromium/issues/detail?id=86742
- blink::WebDataSource* data_source = frame->provisionalDataSource() ?
- frame->provisionalDataSource() : frame->dataSource();
- CHECK(data_source);
- return GURL(data_source->request().url());
-}
-
-void UserScriptSlave::InjectScripts(WebFrame* frame,
- UserScript::RunLocation location) {
- GURL data_source_url = GetDataSourceURLForFrame(frame);
- if (data_source_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;
-
- 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)
- continue;
-
- // 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,
- data_source_url,
- frame->top()->document().url(),
- kNoTabId,
- script,
- kNoProcessId,
- NULL)) {
- continue;
- }
-
- // We rely on WebCore for CSS injection, but it's still useful to know how
- // many css files there are.
- if (location == UserScript::DOCUMENT_START)
- num_css += script->css_scripts().size();
-
- if (script->run_location() == location) {
- 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()));
- }
- }
-
- 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());
- }
- }
- }
-
- // Notify the browser if any extensions are now executing scripts.
- if (!extensions_executing_scripts.empty()) {
- blink::WebFrame* top_frame = frame->top();
- content::RenderView* render_view =
- content::RenderView::FromWebView(top_frame->view());
- render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
- render_view->GetRoutingID(),
- extensions_executing_scripts,
- render_view->GetPageId(),
- GetDataSourceURLForFrame(top_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());
- } 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());
- } 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());
- } else {
- NOTREACHED();
- }
-}
-
-} // namespace extensions
+// Copyright (c) 2012 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 "chrome/renderer/extensions/user_script_slave.h"
+
+#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 "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/renderer/chrome_render_process_observer.h"
+#include "chrome/renderer/extensions/dom_activity_logger.h"
+#include "chrome/renderer/extensions/extension_groups.h"
+#include "chrome/renderer/isolated_world_ids.h"
+#include "content/public/renderer/render_thread.h"
+#include "content/public/renderer/render_view.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/manifest_handlers/csp_info.h"
+#include "extensions/common/permissions/permissions_data.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/WebDataSource.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 = chrome::ISOLATED_WORLD_ID_EXTENSIONS;
+
+ 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;
+ }
+
+ 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;
+ InitializeIsolatedWorld(new_id, extension);
+ frame->setIsolatedWorldSecurityOrigin(
+ new_id,
+ WebSecurityOrigin::create(extension->url()));
+ frame->setIsolatedWorldContentSecurityPolicy(
+ new_id,
+ WebString::fromUTF8(CSPInfo::GetContentSecurityPolicy(extension)));
+ return new_id;
+}
+
+std::string UserScriptSlave::GetExtensionIdForIsolatedWorld(
+ int isolated_world_id) {
+ for (IsolatedWorldMap::iterator iter = isolated_world_ids_.begin();
+ iter != isolated_world_ids_.end(); ++iter) {
+ if (iter->second == isolated_world_id)
+ return iter->first;
+ }
+ return std::string();
+}
+
+// static
+void UserScriptSlave::InitializeIsolatedWorld(int isolated_world_id,
+ const Extension* extension) {
+ const URLPatternSet& permissions =
+ PermissionsData::GetEffectiveHostPermissions(extension);
+ for (URLPatternSet::const_iterator i = permissions.begin();
+ i != permissions.end(); ++i) {
+ const char* schemes[] = {
+ content::kHttpScheme,
+ content::kHttpsScheme,
+ content::kFileScheme,
+ content::kChromeUIScheme,
+ };
+ for (size_t j = 0; j < arraysize(schemes); ++j) {
+ if (i->MatchesScheme(schemes[j])) {
+ WebSecurityPolicy::addOriginAccessWhitelistEntry(
+ extension->url(),
+ WebString::fromUTF8(schemes[j]),
+ WebString::fromUTF8(i->host()),
+ i->match_subdomains());
+ }
+ }
+ }
+}
+
+void UserScriptSlave::RemoveIsolatedWorld(const std::string& extension_id) {
+ isolated_world_ids_.erase(extension_id);
+}
+
+UserScriptSlave::UserScriptSlave(const ExtensionSet* extensions)
+ : script_deleter_(&scripts_), extensions_(extensions) {
+ api_js_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_GREASEMONKEY_API_JS);
+}
+
+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());
+ }
+}
+
+bool UserScriptSlave::UpdateScripts(base::SharedMemoryHandle shared_memory) {
+ scripts_.clear();
+
+ bool only_inject_incognito =
+ ChromeRenderProcessObserver::is_incognito_process();
+
+ // Create the shared memory object (read only).
+ shared_memory_.reset(new base::SharedMemory(shared_memory, true));
+ if (!shared_memory_.get())
+ return false;
+
+ // First get the size of the memory block.
+ if (!shared_memory_->Map(sizeof(Pickle::Header)))
+ return false;
+ Pickle::Header* pickle_header =
+ reinterpret_cast<Pickle::Header*>(shared_memory_->memory());
+
+ // Now map in the rest of the block.
+ int pickle_size = sizeof(Pickle::Header) + pickle_header->payload_size;
+ shared_memory_->Unmap();
+ if (!shared_memory_->Map(pickle_size))
+ return false;
+
+ // Unpickle scripts.
+ uint64 num_scripts = 0;
+ Pickle pickle(reinterpret_cast<char*>(shared_memory_->memory()),
+ pickle_size);
+ PickleIterator iter(pickle);
+ CHECK(pickle.ReadUInt64(&iter, &num_scripts));
+
+ scripts_.reserve(num_scripts);
+ for (uint64 i = 0; i < num_scripts; ++i) {
+ scripts_.push_back(new UserScript());
+ UserScript* script = scripts_.back();
+ script->Unpickle(pickle, &iter);
+
+ // Note that this is a pointer into shared memory. We don't own it. It gets
+ // cleared up when the last renderer or browser process drops their
+ // reference to the shared memory.
+ for (size_t j = 0; j < script->js_scripts().size(); ++j) {
+ const char* body = NULL;
+ int body_length = 0;
+ CHECK(pickle.ReadData(&iter, &body, &body_length));
+ script->js_scripts()[j].set_external_content(
+ base::StringPiece(body, body_length));
+ }
+ for (size_t j = 0; j < script->css_scripts().size(); ++j) {
+ const char* body = NULL;
+ int body_length = 0;
+ CHECK(pickle.ReadData(&iter, &body, &body_length));
+ script->css_scripts()[j].set_external_content(
+ 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();
+ }
+ }
+
+ // Push user styles down into WebCore
+ RenderThread::Get()->EnsureWebKitInitialized();
+ WebView::removeInjectedStyleSheets();
+ for (size_t i = 0; i < scripts_.size(); ++i) {
+ UserScript* script = scripts_[i];
+ if (script->css_scripts().empty())
+ continue;
+
+ WebVector<WebString> patterns;
+ std::vector<WebString> temp_patterns;
+ const URLPatternSet& url_patterns = script->url_patterns();
+ for (URLPatternSet::const_iterator k = url_patterns.begin();
+ k != url_patterns.end(); ++k) {
+ URLPatternList explicit_patterns = k->ConvertToExplicitSchemes();
+ for (size_t m = 0; m < explicit_patterns.size(); ++m) {
+ temp_patterns.push_back(WebString::fromUTF8(
+ explicit_patterns[m].GetAsString()));
+ }
+ }
+ patterns.assign(temp_patterns);
+
+ for (size_t j = 0; j < script->css_scripts().size(); ++j) {
+ const UserScript::File& file = scripts_[i]->css_scripts()[j];
+ std::string content = file.GetContent().as_string();
+
+ WebView::injectStyleSheet(
+ WebString::fromUTF8(content),
+ patterns,
+ script->match_all_frames() ?
+ WebView::InjectStyleInAllFrames :
+ WebView::InjectStyleInTopFrameOnly);
+ }
+ }
+
+ return true;
+}
+
+GURL UserScriptSlave::GetDataSourceURLForFrame(const WebFrame* frame) {
+ // Normally we would use frame->document().url() to determine the document's
+ // URL, but to decide whether to inject a content script, we use the URL from
+ // the data source. This "quirk" helps prevents content scripts from
+ // inadvertently adding DOM elements to the compose iframe in Gmail because
+ // the compose iframe's dataSource URL is about:blank, but the document URL
+ // changes to match the parent document after Gmail document.writes into
+ // it to create the editor.
+ // http://code.google.com/p/chromium/issues/detail?id=86742
+ blink::WebDataSource* data_source = frame->provisionalDataSource() ?
+ frame->provisionalDataSource() : frame->dataSource();
+ CHECK(data_source);
+ return GURL(data_source->request().url());
+}
+
+void UserScriptSlave::InjectScripts(WebFrame* frame,
+ UserScript::RunLocation location) {
+ GURL data_source_url = GetDataSourceURLForFrame(frame);
+ if (data_source_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;
+
+ 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)
+ continue;
+
+ // 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,
+ data_source_url,
+ frame->top()->document().url(),
+ kNoTabId,
+ script,
+ kNoProcessId,
+ NULL)) {
+ continue;
+ }
+
+ // We rely on WebCore for CSS injection, but it's still useful to know how
+ // many css files there are.
+ if (location == UserScript::DOCUMENT_START)
+ num_css += script->css_scripts().size();
+
+ if (script->run_location() == location) {
+ 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()));
+ }
+ }
+
+ 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());
+ }
+ }
+ }
+
+ // Notify the browser if any extensions are now executing scripts.
+ if (!extensions_executing_scripts.empty()) {
+ blink::WebFrame* top_frame = frame->top();
+ content::RenderView* render_view =
+ content::RenderView::FromWebView(top_frame->view());
+ if (render_view) {
+ render_view->Send(new ExtensionHostMsg_ContentScriptsExecuting(
+ render_view->GetRoutingID(),
+ extensions_executing_scripts,
+ render_view->GetPageId(),
+ GetDataSourceURLForFrame(top_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());
+ } 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());
+ } 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());
+ } else {
+ NOTREACHED();
+ }
+}
+
+} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698