| // Copyright 2015 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "extensions/browser/embedder_user_script_loader.h" | 
 |  | 
 | #include <set> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/task/sequenced_task_runner.h" | 
 | #include "base/task/thread_pool.h" | 
 | #include "components/guest_view/buildflags/buildflags.h" | 
 | #include "content/public/browser/browser_context.h" | 
 | #include "extensions/browser/url_fetcher.h" | 
 | #include "extensions/browser/user_script_loader.h" | 
 | #include "extensions/buildflags/buildflags.h" | 
 | #include "extensions/common/mojom/host_id.mojom.h" | 
 | #include "extensions/common/user_script.h" | 
 | #include "url/gurl.h" | 
 |  | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 | #include "extensions/browser/guest_view/web_view/controlled_frame_embedder_url_fetcher.h" | 
 | #include "extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.h" | 
 | #endif | 
 |  | 
 | namespace { | 
 |  | 
 | void SerializeOnBlockingTask( | 
 |     scoped_refptr<base::SequencedTaskRunner> task_runner, | 
 |     extensions::UserScriptList user_scripts, | 
 |     extensions::UserScriptLoader::LoadScriptsCallback callback) { | 
 |   base::ReadOnlySharedMemoryRegion memory = | 
 |       extensions::UserScriptLoader::Serialize(user_scripts); | 
 |  | 
 |   task_runner->PostTask( | 
 |       FROM_HERE, base::BindOnce(std::move(callback), std::move(user_scripts), | 
 |                                 std::move(memory))); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | struct EmbedderUserScriptLoader::UserScriptRenderInfo { | 
 |   const int render_process_id; | 
 |   const int render_frame_id; | 
 | }; | 
 |  | 
 | EmbedderUserScriptLoader::EmbedderUserScriptLoader( | 
 |     content::BrowserContext* browser_context, | 
 |     const extensions::mojom::HostID& host_id) | 
 |     : UserScriptLoader(browser_context, host_id), complete_fetchers_(0) { | 
 |   SetReady(true); | 
 | } | 
 |  | 
 | EmbedderUserScriptLoader::~EmbedderUserScriptLoader() { | 
 | } | 
 |  | 
 | void EmbedderUserScriptLoader::AddScripts(extensions::UserScriptList scripts, | 
 |                                           int render_process_id, | 
 |                                           int render_frame_id, | 
 |                                           ScriptsLoadedCallback callback) { | 
 |   UserScriptRenderInfo info{.render_process_id = render_process_id, | 
 |                             .render_frame_id = render_frame_id}; | 
 |   for (const std::unique_ptr<extensions::UserScript>& script : scripts) { | 
 |     script_render_info_map_.emplace(script->id(), info); | 
 |   } | 
 |  | 
 |   extensions::UserScriptLoader::AddScripts(std::move(scripts), | 
 |                                            std::move(callback)); | 
 | } | 
 |  | 
 | void EmbedderUserScriptLoader::LoadScripts( | 
 |     extensions::UserScriptList user_scripts, | 
 |     const std::set<std::string>& added_script_ids, | 
 |     LoadScriptsCallback callback) { | 
 |   DCHECK(user_scripts_cache_.empty()) << "Loading scripts in flight."; | 
 |   user_scripts_cache_ = std::move(user_scripts); | 
 |   scripts_loaded_callback_ = std::move(callback); | 
 |  | 
 |   // The total number of the tasks is used to trace whether all the fetches | 
 |   // are complete. Therefore, we store all the fetcher pointers in |fetchers_| | 
 |   // before we get this number. Once we get the total number, start each | 
 |   // fetch tasks. | 
 |   DCHECK_EQ(0u, complete_fetchers_); | 
 |  | 
 |   for (const std::unique_ptr<extensions::UserScript>& script : | 
 |        user_scripts_cache_) { | 
 |     if (added_script_ids.count(script->id()) == 0) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     auto iter = script_render_info_map_.find(script->id()); | 
 |     CHECK(iter != script_render_info_map_.end()); | 
 |     int render_process_id = iter->second.render_process_id; | 
 |     int render_frame_id = iter->second.render_frame_id; | 
 |  | 
 |     CreateEmbedderURLFetchers(script->js_scripts(), render_process_id, | 
 |                               render_frame_id); | 
 |     CreateEmbedderURLFetchers(script->css_scripts(), render_process_id, | 
 |                               render_frame_id); | 
 |  | 
 |     script_render_info_map_.erase(iter); | 
 |   } | 
 |  | 
 |   // If no fetch is needed, call OnEmbedderURLFetchComplete directly. | 
 |   if (fetchers_.empty()) { | 
 |     OnEmbedderURLFetchComplete(); | 
 |     return; | 
 |   } | 
 |   for (const auto& fetcher : fetchers_) { | 
 |     fetcher->Start(); | 
 |   } | 
 | } | 
 |  | 
 | void EmbedderUserScriptLoader::CreateEmbedderURLFetchers( | 
 |     const extensions::UserScript::ContentList& contents, | 
 |     int render_process_id, | 
 |     int render_frame_id) { | 
 |   for (const std::unique_ptr<extensions::UserScript::Content>& content : | 
 |        contents) { | 
 |     if (!content->GetContent().empty()) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     std::unique_ptr<extensions::URLFetcher> fetcher; | 
 |     switch (host_id_.type) { | 
 |       case extensions::mojom::HostID::HostType::kWebUi: | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 |         fetcher = std::make_unique<extensions::WebUIURLFetcher>( | 
 |             render_process_id, render_frame_id, content->url(), | 
 |             base::BindOnce( | 
 |                 &EmbedderUserScriptLoader::OnSingleEmbedderURLFetchComplete, | 
 |                 weak_ptr_factory_.GetWeakPtr(), content.get())); | 
 |         break; | 
 | #else | 
 |         NOTREACHED(); | 
 | #endif | 
 |       case extensions::mojom::HostID::HostType::kControlledFrameEmbedder: | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 |         fetcher = std::make_unique< | 
 |             extensions::ControlledFrameEmbedderURLFetcher>( | 
 |             render_process_id, render_frame_id, content->url(), | 
 |             base::BindOnce( | 
 |                 &EmbedderUserScriptLoader::OnSingleEmbedderURLFetchComplete, | 
 |                 weak_ptr_factory_.GetWeakPtr(), content.get())); | 
 |         break; | 
 | #else | 
 |         NOTREACHED(); | 
 | #endif | 
 |       case extensions::mojom::HostID::HostType::kExtensions: | 
 |         NOTREACHED(); | 
 |     } | 
 |     fetchers_.push_back(std::move(fetcher)); | 
 |   } | 
 | } | 
 |  | 
 | void EmbedderUserScriptLoader::OnSingleEmbedderURLFetchComplete( | 
 |     extensions::UserScript::Content* content, | 
 |     bool success, | 
 |     std::unique_ptr<std::string> data) { | 
 |   if (success) { | 
 |     // Remove BOM from |data|. | 
 |     if (base::StartsWith(*data, base::kUtf8ByteOrderMark)) { | 
 |       data->erase(0, strlen(base::kUtf8ByteOrderMark)); | 
 |     } | 
 |     content->set_content(std::move(*data)); | 
 |   } | 
 |  | 
 |   ++complete_fetchers_; | 
 |   if (complete_fetchers_ == fetchers_.size()) { | 
 |     complete_fetchers_ = 0; | 
 |     OnEmbedderURLFetchComplete(); | 
 |     fetchers_.clear(); | 
 |   } | 
 | } | 
 |  | 
 | void EmbedderUserScriptLoader::OnEmbedderURLFetchComplete() { | 
 |   base::ThreadPool::PostTask( | 
 |       FROM_HERE, {base::MayBlock()}, | 
 |       base::BindOnce(&SerializeOnBlockingTask, | 
 |                      base::SequencedTaskRunner::GetCurrentDefault(), | 
 |                      std::move(user_scripts_cache_), | 
 |                      std::move(scripts_loaded_callback_))); | 
 | } |