user_script_set.cc 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. #include "extensions/renderer/user_script_set.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/debug/alias.h"
  9. #include "base/memory/ref_counted.h"
  10. #include "base/observer_list.h"
  11. #include "base/strings/strcat.h"
  12. #include "content/public/common/url_constants.h"
  13. #include "content/public/renderer/render_frame.h"
  14. #include "content/public/renderer/render_thread.h"
  15. #include "extensions/common/extension.h"
  16. #include "extensions/common/extensions_client.h"
  17. #include "extensions/common/mojom/host_id.mojom.h"
  18. #include "extensions/renderer/extension_injection_host.h"
  19. #include "extensions/renderer/extensions_renderer_client.h"
  20. #include "extensions/renderer/injection_host.h"
  21. #include "extensions/renderer/script_context.h"
  22. #include "extensions/renderer/script_injection.h"
  23. #include "extensions/renderer/user_script_injector.h"
  24. #include "extensions/renderer/web_ui_injection_host.h"
  25. #include "third_party/blink/public/web/web_document.h"
  26. #include "third_party/blink/public/web/web_local_frame.h"
  27. #include "url/gurl.h"
  28. namespace extensions {
  29. namespace {
  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. // Maximum number of total content scripts we allow (across all extensions).
  35. // The limit exists to diagnose https://crbug.com/723381. The number is
  36. // arbitrarily chosen.
  37. // TODO(lazyboy): Remove when the bug is fixed.
  38. const uint32_t kNumScriptsArbitraryMax = 100000u;
  39. GURL GetDocumentUrlForFrame(blink::WebLocalFrame* frame) {
  40. GURL data_source_url = ScriptContext::GetDocumentLoaderURLForFrame(frame);
  41. if (!data_source_url.is_empty() && frame->IsViewSourceModeEnabled()) {
  42. data_source_url = GURL(content::kViewSourceScheme + std::string(":") +
  43. data_source_url.spec());
  44. }
  45. return data_source_url;
  46. }
  47. } // namespace
  48. UserScriptSet::UserScriptSet(mojom::HostID host_id)
  49. : host_id_(std::move(host_id)) {}
  50. UserScriptSet::~UserScriptSet() {
  51. for (auto& observer : observers_)
  52. observer.OnUserScriptSetDestroyed();
  53. }
  54. void UserScriptSet::AddObserver(Observer* observer) {
  55. observers_.AddObserver(observer);
  56. }
  57. void UserScriptSet::RemoveObserver(Observer* observer) {
  58. observers_.RemoveObserver(observer);
  59. }
  60. void UserScriptSet::GetInjections(
  61. std::vector<std::unique_ptr<ScriptInjection>>* injections,
  62. content::RenderFrame* render_frame,
  63. int tab_id,
  64. mojom::RunLocation run_location,
  65. bool log_activity) {
  66. GURL document_url = GetDocumentUrlForFrame(render_frame->GetWebFrame());
  67. for (const std::unique_ptr<UserScript>& script : scripts_) {
  68. std::unique_ptr<ScriptInjection> injection = GetInjectionForScript(
  69. script.get(), render_frame, tab_id, run_location, document_url,
  70. false /* is_declarative */, log_activity);
  71. if (injection.get())
  72. injections->push_back(std::move(injection));
  73. }
  74. }
  75. bool UserScriptSet::UpdateUserScripts(
  76. base::ReadOnlySharedMemoryRegion shared_memory) {
  77. bool only_inject_incognito =
  78. ExtensionsRendererClient::Get()->IsIncognitoProcess();
  79. // Create the shared memory mapping.
  80. shared_memory_mapping_ = shared_memory.Map();
  81. if (!shared_memory.IsValid())
  82. return false;
  83. // First get the size of the memory block.
  84. const base::Pickle::Header* pickle_header =
  85. shared_memory_mapping_.GetMemoryAs<base::Pickle::Header>();
  86. if (!pickle_header)
  87. return false;
  88. // Now read in the rest of the block.
  89. size_t pickle_size =
  90. sizeof(base::Pickle::Header) + pickle_header->payload_size;
  91. // Unpickle scripts.
  92. uint32_t num_scripts = 0;
  93. auto memory = shared_memory_mapping_.GetMemoryAsSpan<char>(pickle_size);
  94. if (!memory.size())
  95. return false;
  96. base::Pickle pickle(memory.data(), pickle_size);
  97. base::PickleIterator iter(pickle);
  98. base::debug::Alias(&pickle_size);
  99. CHECK(iter.ReadUInt32(&num_scripts));
  100. // Sometimes the shared memory contents seem to be corrupted
  101. // (https://crbug.com/723381). Set an arbitrary max limit to the number of
  102. // scripts so that we don't add OOM noise to crash reports.
  103. CHECK_LT(num_scripts, kNumScriptsArbitraryMax);
  104. scripts_.clear();
  105. script_sources_.clear();
  106. scripts_.reserve(num_scripts);
  107. for (uint32_t i = 0; i < num_scripts; ++i) {
  108. std::unique_ptr<UserScript> script(new UserScript());
  109. script->Unpickle(pickle, &iter);
  110. // Note that this is a pointer into shared memory. We don't own it. It
  111. // gets cleared up when the last renderer or browser process drops their
  112. // reference to the shared memory.
  113. for (const auto& js_script : script->js_scripts()) {
  114. const char* body = nullptr;
  115. size_t body_length = 0;
  116. CHECK(iter.ReadData(&body, &body_length));
  117. js_script->set_external_content(base::StringPiece(body, body_length));
  118. }
  119. for (const auto& css_script : script->css_scripts()) {
  120. const char* body = nullptr;
  121. size_t body_length = 0;
  122. CHECK(iter.ReadData(&body, &body_length));
  123. css_script->set_external_content(base::StringPiece(body, body_length));
  124. }
  125. if (only_inject_incognito && !script->is_incognito_enabled())
  126. continue; // This script shouldn't run in an incognito tab.
  127. scripts_.push_back(std::move(script));
  128. }
  129. for (auto& observer : observers_)
  130. observer.OnUserScriptsUpdated();
  131. return true;
  132. }
  133. void UserScriptSet::ClearUserScripts() {
  134. scripts_.clear();
  135. script_sources_.clear();
  136. for (auto& observer : observers_)
  137. observer.OnUserScriptsUpdated();
  138. }
  139. std::unique_ptr<ScriptInjection> UserScriptSet::GetDeclarativeScriptInjection(
  140. const std::string& script_id,
  141. content::RenderFrame* render_frame,
  142. int tab_id,
  143. mojom::RunLocation run_location,
  144. const GURL& document_url,
  145. bool log_activity) {
  146. for (const std::unique_ptr<UserScript>& script : scripts_) {
  147. if (script->id() == script_id) {
  148. return GetInjectionForScript(script.get(), render_frame, tab_id,
  149. run_location, document_url,
  150. true /* is_declarative */, log_activity);
  151. }
  152. }
  153. return nullptr;
  154. }
  155. std::unique_ptr<ScriptInjection> UserScriptSet::GetInjectionForScript(
  156. const UserScript* script,
  157. content::RenderFrame* render_frame,
  158. int tab_id,
  159. mojom::RunLocation run_location,
  160. const GURL& document_url,
  161. bool is_declarative,
  162. bool log_activity) {
  163. std::unique_ptr<ScriptInjection> injection;
  164. std::unique_ptr<const InjectionHost> injection_host;
  165. blink::WebLocalFrame* web_frame = render_frame->GetWebFrame();
  166. if (host_id_.type == mojom::HostID::HostType::kExtensions) {
  167. injection_host = ExtensionInjectionHost::Create(host_id_.id);
  168. if (!injection_host)
  169. return injection;
  170. } else {
  171. DCHECK_EQ(host_id_.type, mojom::HostID::HostType::kWebUi);
  172. injection_host = std::make_unique<WebUIInjectionHost>(host_id_);
  173. }
  174. GURL effective_document_url =
  175. ScriptContext::GetEffectiveDocumentURLForInjection(
  176. web_frame, document_url, script->match_origin_as_fallback());
  177. bool is_subframe = !web_frame->IsOutermostMainFrame();
  178. if (!script->MatchesDocument(effective_document_url, is_subframe))
  179. return injection;
  180. // Extension dynamic scripts are treated as declarative scripts and should use
  181. // host permissions instead of scriptable hosts to determine if they should be
  182. // injected into a frame.
  183. bool is_extension_dynamic_script =
  184. (host_id_.type == mojom::HostID::HostType::kExtensions) &&
  185. !script->IsIDGenerated();
  186. std::unique_ptr<ScriptInjector> injector(new UserScriptInjector(
  187. script, this, is_declarative || is_extension_dynamic_script));
  188. if (injector->CanExecuteOnFrame(injection_host.get(), web_frame, tab_id) ==
  189. PermissionsData::PageAccess::kDenied) {
  190. return injection;
  191. }
  192. bool inject_css = !script->css_scripts().empty() &&
  193. run_location == mojom::RunLocation::kDocumentStart;
  194. bool inject_js =
  195. !script->js_scripts().empty() && script->run_location() == run_location;
  196. if (inject_css || inject_js) {
  197. injection = std::make_unique<ScriptInjection>(
  198. std::move(injector), render_frame, std::move(injection_host),
  199. run_location, log_activity);
  200. }
  201. return injection;
  202. }
  203. blink::WebString UserScriptSet::GetJsSource(const UserScript::File& file,
  204. bool emulate_greasemonkey) {
  205. const GURL& url = file.url();
  206. auto iter = script_sources_.find(url);
  207. if (iter != script_sources_.end())
  208. return iter->second;
  209. base::StringPiece script_content = file.GetContent();
  210. blink::WebString source;
  211. if (emulate_greasemonkey) {
  212. // We add this dumb function wrapper for user scripts to emulate what
  213. // Greasemonkey does. |script_content| becomes:
  214. // concat(kUserScriptHead, script_content, kUserScriptTail).
  215. std::string content =
  216. base::StrCat({kUserScriptHead, script_content, kUserScriptTail});
  217. source = blink::WebString::FromUTF8(content);
  218. } else {
  219. source = blink::WebString::FromUTF8(script_content.data(),
  220. script_content.length());
  221. }
  222. script_sources_[url] = source;
  223. return source;
  224. }
  225. blink::WebString UserScriptSet::GetCssSource(const UserScript::File& file) {
  226. const GURL& url = file.url();
  227. auto iter = script_sources_.find(url);
  228. if (iter != script_sources_.end())
  229. return iter->second;
  230. base::StringPiece script_content = file.GetContent();
  231. return script_sources_
  232. .insert(std::make_pair(
  233. url, blink::WebString::FromUTF8(script_content.data(),
  234. script_content.length())))
  235. .first->second;
  236. }
  237. } // namespace extensions