OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "extensions/renderer/script_injection.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/lazy_instance.h" |
| 10 #include "base/metrics/histogram.h" |
| 11 #include "content/public/common/url_constants.h" |
| 12 #include "extensions/common/extension.h" |
| 13 #include "extensions/common/extension_messages.h" |
| 14 #include "extensions/common/permissions/permissions_data.h" |
| 15 #include "extensions/renderer/dom_activity_logger.h" |
| 16 #include "extensions/renderer/extension_groups.h" |
| 17 #include "extensions/renderer/script_context.h" |
| 18 #include "extensions/renderer/user_script_slave.h" |
| 19 #include "grit/renderer_resources.h" |
| 20 #include "third_party/WebKit/public/web/WebDocument.h" |
| 21 #include "third_party/WebKit/public/web/WebFrame.h" |
| 22 #include "third_party/WebKit/public/web/WebScriptSource.h" |
| 23 #include "ui/base/resource/resource_bundle.h" |
| 24 #include "url/gurl.h" |
| 25 |
| 26 namespace extensions { |
| 27 |
| 28 namespace { |
| 29 |
| 30 // These two strings are injected before and after the Greasemonkey API and |
| 31 // user script to wrap it in an anonymous scope. |
| 32 const char kUserScriptHead[] = "(function (unsafeWindow) {\n"; |
| 33 const char kUserScriptTail[] = "\n})(window);"; |
| 34 |
| 35 // Greasemonkey API source that is injected with the scripts. |
| 36 struct GreasemonkeyApiJsString { |
| 37 GreasemonkeyApiJsString(); |
| 38 blink::WebScriptSource source; |
| 39 }; |
| 40 |
| 41 // The below constructor, monstrous as it is, just makes a WebScriptSource from |
| 42 // the GreasemonkeyApiJs resource. |
| 43 GreasemonkeyApiJsString::GreasemonkeyApiJsString() |
| 44 : source(blink::WebScriptSource(blink::WebString::fromUTF8( |
| 45 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 46 IDR_GREASEMONKEY_API_JS).as_string()))) { |
| 47 } |
| 48 |
| 49 base::LazyInstance<GreasemonkeyApiJsString> g_greasemonkey_api = |
| 50 LAZY_INSTANCE_INITIALIZER; |
| 51 |
| 52 } // namespace |
| 53 |
| 54 ScriptInjection::ScriptsRunInfo::ScriptsRunInfo() : num_css(0u), num_js(0u) { |
| 55 } |
| 56 |
| 57 ScriptInjection::ScriptsRunInfo::~ScriptsRunInfo() { |
| 58 } |
| 59 |
| 60 // static |
| 61 GURL ScriptInjection::GetDocumentUrlForFrame(blink::WebFrame* frame) { |
| 62 GURL data_source_url = ScriptContext::GetDataSourceURLForFrame(frame); |
| 63 if (!data_source_url.is_empty() && frame->isViewSourceModeEnabled()) { |
| 64 data_source_url = GURL(content::kViewSourceScheme + std::string(":") + |
| 65 data_source_url.spec()); |
| 66 } |
| 67 |
| 68 return data_source_url; |
| 69 } |
| 70 |
| 71 ScriptInjection::ScriptInjection( |
| 72 scoped_ptr<UserScript> script, |
| 73 UserScriptSlave* user_script_slave) |
| 74 : script_(script.Pass()), |
| 75 extension_id_(script_->extension_id()), |
| 76 user_script_slave_(user_script_slave), |
| 77 is_standalone_or_emulate_greasemonkey_( |
| 78 script_->is_standalone() || script_->emulate_greasemonkey()) { |
| 79 } |
| 80 |
| 81 ScriptInjection::~ScriptInjection() { |
| 82 } |
| 83 |
| 84 bool ScriptInjection::WantsToRun(blink::WebFrame* frame, |
| 85 UserScript::RunLocation run_location, |
| 86 const GURL& document_url) const { |
| 87 if (frame->parent() && !script_->match_all_frames()) |
| 88 return false; // Only match subframes if the script declared it wanted to. |
| 89 |
| 90 const Extension* extension = user_script_slave_->GetExtension(extension_id_); |
| 91 // Since extension info is sent separately from user script info, they can |
| 92 // be out of sync. We just ignore this situation. |
| 93 if (!extension) |
| 94 return false; |
| 95 |
| 96 // Content scripts are not tab-specific. |
| 97 static const int kNoTabId = -1; |
| 98 // We don't have a process id in this context. |
| 99 static const int kNoProcessId = -1; |
| 100 |
| 101 GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( |
| 102 frame, document_url, script_->match_about_blank()); |
| 103 |
| 104 if (!PermissionsData::CanExecuteScriptOnPage(extension, |
| 105 effective_document_url, |
| 106 frame->top()->document().url(), |
| 107 kNoTabId, |
| 108 script_.get(), |
| 109 kNoProcessId, |
| 110 NULL /* ignore error */)) { |
| 111 return false; |
| 112 } |
| 113 |
| 114 return ShouldInjectCSS(run_location) || ShouldInjectJS(run_location); |
| 115 } |
| 116 |
| 117 void ScriptInjection::Inject(blink::WebFrame* frame, |
| 118 UserScript::RunLocation run_location, |
| 119 ScriptsRunInfo* scripts_run_info) const { |
| 120 DCHECK(frame); |
| 121 DCHECK(scripts_run_info); |
| 122 DCHECK(WantsToRun(frame, run_location, GetDocumentUrlForFrame(frame))); |
| 123 DCHECK(user_script_slave_->GetExtension(extension_id_)); |
| 124 |
| 125 if (ShouldInjectCSS(run_location)) |
| 126 InjectCSS(frame, scripts_run_info); |
| 127 if (ShouldInjectJS(run_location)) |
| 128 InjectJS(frame, scripts_run_info); |
| 129 } |
| 130 |
| 131 bool ScriptInjection::ShouldInjectJS(UserScript::RunLocation run_location) |
| 132 const { |
| 133 return !script_->js_scripts().empty() && |
| 134 script_->run_location() == run_location; |
| 135 } |
| 136 |
| 137 bool ScriptInjection::ShouldInjectCSS(UserScript::RunLocation run_location) |
| 138 const { |
| 139 return !script_->css_scripts().empty() && |
| 140 run_location == UserScript::DOCUMENT_START; |
| 141 } |
| 142 |
| 143 void ScriptInjection::InjectJS(blink::WebFrame* frame, |
| 144 ScriptsRunInfo* scripts_run_info) const { |
| 145 const UserScript::FileList& js_scripts = script_->js_scripts(); |
| 146 std::vector<blink::WebScriptSource> sources; |
| 147 scripts_run_info->num_js += js_scripts.size(); |
| 148 for (UserScript::FileList::const_iterator iter = js_scripts.begin(); |
| 149 iter != js_scripts.end(); |
| 150 ++iter) { |
| 151 std::string content = iter->GetContent().as_string(); |
| 152 |
| 153 // We add this dumb function wrapper for standalone user script to |
| 154 // emulate what Greasemonkey does. |
| 155 // TODO(aa): I think that maybe "is_standalone" scripts don't exist |
| 156 // anymore. Investigate. |
| 157 if (is_standalone_or_emulate_greasemonkey_) { |
| 158 content.insert(0, kUserScriptHead); |
| 159 content += kUserScriptTail; |
| 160 } |
| 161 sources.push_back(blink::WebScriptSource( |
| 162 blink::WebString::fromUTF8(content), iter->url())); |
| 163 } |
| 164 |
| 165 // Emulate Greasemonkey API for scripts that were converted to extensions |
| 166 // and "standalone" user scripts. |
| 167 if (is_standalone_or_emulate_greasemonkey_) |
| 168 sources.insert(sources.begin(), g_greasemonkey_api.Get().source); |
| 169 |
| 170 int isolated_world_id = |
| 171 user_script_slave_->GetIsolatedWorldIdForExtension( |
| 172 user_script_slave_->GetExtension(extension_id_), frame); |
| 173 base::ElapsedTimer exec_timer; |
| 174 DOMActivityLogger::AttachToWorld(isolated_world_id, extension_id_); |
| 175 frame->executeScriptInIsolatedWorld(isolated_world_id, |
| 176 &sources.front(), |
| 177 sources.size(), |
| 178 EXTENSION_GROUP_CONTENT_SCRIPTS); |
| 179 UMA_HISTOGRAM_TIMES("Extensions.InjectScriptTime", exec_timer.Elapsed()); |
| 180 |
| 181 for (std::vector<blink::WebScriptSource>::const_iterator iter = |
| 182 sources.begin(); |
| 183 iter != sources.end(); |
| 184 ++iter) { |
| 185 scripts_run_info->executing_scripts[extension_id_].insert( |
| 186 GURL(iter->url).path()); |
| 187 } |
| 188 } |
| 189 |
| 190 void ScriptInjection::InjectCSS(blink::WebFrame* frame, |
| 191 ScriptsRunInfo* scripts_run_info) const { |
| 192 const UserScript::FileList& css_scripts = script_->css_scripts(); |
| 193 scripts_run_info->num_css += css_scripts.size(); |
| 194 for (UserScript::FileList::const_iterator iter = css_scripts.begin(); |
| 195 iter != css_scripts.end(); |
| 196 ++iter) { |
| 197 frame->document().insertStyleSheet( |
| 198 blink::WebString::fromUTF8(iter->GetContent().as_string())); |
| 199 } |
| 200 } |
| 201 |
| 202 } // namespace extensions |
OLD | NEW |