| 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
|
|
|