web_ui_user_script_loader.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright 2015 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/browser/web_ui_user_script_loader.h"
  5. #include <set>
  6. #include <string>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/memory/ref_counted.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/task/sequenced_task_runner.h"
  12. #include "base/task/thread_pool.h"
  13. #include "base/threading/sequenced_task_runner_handle.h"
  14. #include "content/public/browser/browser_context.h"
  15. #include "extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.h"
  16. #include "extensions/common/mojom/host_id.mojom.h"
  17. #include "url/gurl.h"
  18. namespace {
  19. void SerializeOnBlockingTask(
  20. scoped_refptr<base::SequencedTaskRunner> task_runner,
  21. std::unique_ptr<extensions::UserScriptList> user_scripts,
  22. extensions::UserScriptLoader::LoadScriptsCallback callback) {
  23. base::ReadOnlySharedMemoryRegion memory =
  24. extensions::UserScriptLoader::Serialize(*user_scripts);
  25. task_runner->PostTask(
  26. FROM_HERE, base::BindOnce(std::move(callback), std::move(user_scripts),
  27. std::move(memory)));
  28. }
  29. } // namespace
  30. struct WebUIUserScriptLoader::UserScriptRenderInfo {
  31. const int render_process_id;
  32. const int render_frame_id;
  33. UserScriptRenderInfo(int render_process_id, int render_frame_id)
  34. : render_process_id(render_process_id),
  35. render_frame_id(render_frame_id) {}
  36. };
  37. WebUIUserScriptLoader::WebUIUserScriptLoader(
  38. content::BrowserContext* browser_context,
  39. const GURL& url)
  40. : UserScriptLoader(browser_context,
  41. extensions::mojom::HostID(
  42. extensions::mojom::HostID::HostID::HostType::kWebUi,
  43. url.spec())),
  44. complete_fetchers_(0) {
  45. SetReady(true);
  46. }
  47. WebUIUserScriptLoader::~WebUIUserScriptLoader() {
  48. }
  49. void WebUIUserScriptLoader::AddScripts(
  50. std::unique_ptr<extensions::UserScriptList> scripts,
  51. int render_process_id,
  52. int render_frame_id,
  53. ScriptsLoadedCallback callback) {
  54. UserScriptRenderInfo info(render_process_id, render_frame_id);
  55. for (const std::unique_ptr<extensions::UserScript>& script : *scripts)
  56. script_render_info_map_.emplace(script->id(), info);
  57. extensions::UserScriptLoader::AddScripts(std::move(scripts),
  58. std::move(callback));
  59. }
  60. void WebUIUserScriptLoader::LoadScripts(
  61. std::unique_ptr<extensions::UserScriptList> user_scripts,
  62. const std::set<std::string>& added_script_ids,
  63. LoadScriptsCallback callback) {
  64. DCHECK(!user_scripts_cache_) << "Loading scripts in flight.";
  65. user_scripts_cache_.swap(user_scripts);
  66. scripts_loaded_callback_ = std::move(callback);
  67. // The total number of the tasks is used to trace whether all the fetches
  68. // are complete. Therefore, we store all the fetcher pointers in |fetchers_|
  69. // before we get theis number. Once we get the total number, start each
  70. // fetch tasks.
  71. DCHECK_EQ(0u, complete_fetchers_);
  72. for (const std::unique_ptr<extensions::UserScript>& script :
  73. *user_scripts_cache_) {
  74. if (added_script_ids.count(script->id()) == 0)
  75. continue;
  76. auto iter = script_render_info_map_.find(script->id());
  77. DCHECK(iter != script_render_info_map_.end());
  78. int render_process_id = iter->second.render_process_id;
  79. int render_frame_id = iter->second.render_frame_id;
  80. CreateWebUIURLFetchers(script->js_scripts(), render_process_id,
  81. render_frame_id);
  82. CreateWebUIURLFetchers(script->css_scripts(), render_process_id,
  83. render_frame_id);
  84. script_render_info_map_.erase(script->id());
  85. }
  86. // If no fetch is needed, call OnWebUIURLFetchComplete directly.
  87. if (fetchers_.empty()) {
  88. OnWebUIURLFetchComplete();
  89. return;
  90. }
  91. for (const auto& fetcher : fetchers_)
  92. fetcher->Start();
  93. }
  94. void WebUIUserScriptLoader::CreateWebUIURLFetchers(
  95. const extensions::UserScript::FileList& script_files,
  96. int render_process_id,
  97. int render_frame_id) {
  98. for (const std::unique_ptr<extensions::UserScript::File>& script_file :
  99. script_files) {
  100. if (script_file->GetContent().empty()) {
  101. // The WebUIUserScriptLoader owns these WebUIURLFetchers. Once the
  102. // loader is destroyed, all the fetchers will be destroyed. Therefore,
  103. // we are sure it is safe to use base::Unretained(this) here.
  104. // |user_scripts_cache_| retains ownership of the scripts while they are
  105. // being loaded, so passing a raw pointer to |script_file| below to
  106. // WebUIUserScriptLoader is also safe.
  107. std::unique_ptr<WebUIURLFetcher> fetcher(new WebUIURLFetcher(
  108. render_process_id, render_frame_id, script_file->url(),
  109. base::BindOnce(&WebUIUserScriptLoader::OnSingleWebUIURLFetchComplete,
  110. base::Unretained(this), script_file.get())));
  111. fetchers_.push_back(std::move(fetcher));
  112. }
  113. }
  114. }
  115. void WebUIUserScriptLoader::OnSingleWebUIURLFetchComplete(
  116. extensions::UserScript::File* script_file,
  117. bool success,
  118. std::unique_ptr<std::string> data) {
  119. if (success) {
  120. // Remove BOM from |data|.
  121. if (base::StartsWith(*data, base::kUtf8ByteOrderMark,
  122. base::CompareCase::SENSITIVE)) {
  123. script_file->set_content(data->substr(strlen(base::kUtf8ByteOrderMark)));
  124. } else {
  125. // TODO(lazyboy): Script files should take ownership of |data|, i.e. the
  126. // content of the script.
  127. script_file->set_content(*data);
  128. }
  129. }
  130. ++complete_fetchers_;
  131. if (complete_fetchers_ == fetchers_.size()) {
  132. complete_fetchers_ = 0;
  133. OnWebUIURLFetchComplete();
  134. fetchers_.clear();
  135. }
  136. }
  137. void WebUIUserScriptLoader::OnWebUIURLFetchComplete() {
  138. base::ThreadPool::PostTask(
  139. FROM_HERE, {base::MayBlock()},
  140. base::BindOnce(
  141. &SerializeOnBlockingTask, base::SequencedTaskRunnerHandle::Get(),
  142. std::move(user_scripts_cache_), std::move(scripts_loaded_callback_)));
  143. }