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 |