blob: 62edf3ffe5d0c83b5e084fa63ab18c820bd7cd13 [file] [log] [blame]
// Copyright 2015 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 "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
#include "base/memory/ptr_util.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/declarative_user_script_manager.h"
#include "extensions/browser/declarative_user_script_master.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/guest_view/web_view/web_view_constants.h"
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
using content::BrowserThread;
namespace extensions {
WebViewContentScriptManager::WebViewContentScriptManager(
content::BrowserContext* browser_context)
: user_script_loader_observer_(this), browser_context_(browser_context) {
}
WebViewContentScriptManager::~WebViewContentScriptManager() {
}
WebViewContentScriptManager* WebViewContentScriptManager::Get(
content::BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebViewContentScriptManager* manager =
static_cast<WebViewContentScriptManager*>(browser_context->GetUserData(
webview::kWebViewContentScriptManagerKeyName));
if (!manager) {
manager = new WebViewContentScriptManager(browser_context);
browser_context->SetUserData(webview::kWebViewContentScriptManagerKeyName,
base::WrapUnique(manager));
}
return manager;
}
void WebViewContentScriptManager::AddContentScripts(
int embedder_process_id,
content::RenderFrameHost* render_frame_host,
int view_instance_id,
const HostID& host_id,
std::unique_ptr<UserScriptList> scripts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DeclarativeUserScriptMaster* master =
DeclarativeUserScriptManager::Get(browser_context_)
->GetDeclarativeUserScriptMasterByID(host_id);
DCHECK(master);
// We need to update WebViewRenderState in the IO thread if the guest exists.
std::set<int> ids_to_add;
GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
auto iter = guest_content_script_map_.find(key);
// Step 1: finds the entry in guest_content_script_map_ by the given |key|.
// If there isn't any content script added for the given guest yet, insert an
// empty map first.
if (iter == guest_content_script_map_.end()) {
iter = guest_content_script_map_.insert(
iter,
std::pair<GuestMapKey, ContentScriptMap>(key, ContentScriptMap()));
}
// Step 2: updates the guest_content_script_map_.
ContentScriptMap& map = iter->second;
std::set<UserScriptIDPair> to_delete;
for (const std::unique_ptr<UserScript>& script : *scripts) {
auto map_iter = map.find(script->name());
// If a content script has the same name as the new one, remove the old
// script first, and insert the new one.
if (map_iter != map.end()) {
to_delete.insert(map_iter->second);
map.erase(map_iter);
}
map.insert(std::pair<std::string, UserScriptIDPair>(
script->name(), UserScriptIDPair(script->id(), script->host_id())));
ids_to_add.insert(script->id());
}
if (!to_delete.empty())
master->RemoveScripts(to_delete);
// Step 3: makes WebViewContentScriptManager become an observer of the
// |loader| for scripts loaded event.
UserScriptLoader* loader = master->loader();
DCHECK(loader);
if (!user_script_loader_observer_.IsObserving(loader))
user_script_loader_observer_.Add(loader);
// Step 4: adds new scripts to the master.
master->AddScripts(std::move(scripts), embedder_process_id,
render_frame_host->GetRoutingID());
// Step 5: creates an entry in |webview_host_id_map_| for the given
// |embedder_process_id| and |view_instance_id| if it doesn't exist.
auto host_it = webview_host_id_map_.find(key);
if (host_it == webview_host_id_map_.end())
webview_host_id_map_.insert(std::make_pair(key, host_id));
// Step 6: updates WebViewRenderState in the IO thread.
// It is safe to use base::Unretained(WebViewRendererState::GetInstance())
// since WebViewRendererState::GetInstance() always returns a Singleton of
// WebViewRendererState.
if (!ids_to_add.empty()) {
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::IO},
base::Bind(&WebViewRendererState::AddContentScriptIDs,
base::Unretained(WebViewRendererState::GetInstance()),
embedder_process_id, view_instance_id, ids_to_add));
}
}
void WebViewContentScriptManager::RemoveAllContentScriptsForWebView(
int embedder_process_id,
int view_instance_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Look up the host ID for the WebView.
GuestMapKey key = std::make_pair(embedder_process_id, view_instance_id);
auto host_it = webview_host_id_map_.find(key);
// If no entry exists, then this WebView has no content scripts.
if (host_it == webview_host_id_map_.end())
return;
// Remove all content scripts for the WebView.
RemoveContentScripts(embedder_process_id, view_instance_id, host_it->second,
std::vector<std::string>());
webview_host_id_map_.erase(host_it);
}
void WebViewContentScriptManager::RemoveContentScripts(
int embedder_process_id,
int view_instance_id,
const HostID& host_id,
const std::vector<std::string>& script_name_list) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
auto script_map_iter = guest_content_script_map_.find(key);
if (script_map_iter == guest_content_script_map_.end())
return;
DeclarativeUserScriptMaster* master =
DeclarativeUserScriptManager::Get(browser_context_)
->GetDeclarativeUserScriptMasterByID(host_id);
CHECK(master);
// We need to update WebViewRenderState in the IO thread if the guest exists.
std::set<int> ids_to_delete;
std::set<UserScriptIDPair> scripts_to_delete;
// Step 1: removes content scripts from |master| and updates
// |guest_content_script_map_|.
std::map<std::string, UserScriptIDPair>& map = script_map_iter->second;
// If the |script_name_list| is empty, all the content scripts added by the
// guest will be removed; otherwise, removes the scripts in the
// |script_name_list|.
if (script_name_list.empty()) {
auto it = map.begin();
while (it != map.end()) {
scripts_to_delete.insert(it->second);
ids_to_delete.insert(it->second.id);
map.erase(it++);
}
} else {
for (const std::string& name : script_name_list) {
auto iter = map.find(name);
if (iter == map.end())
continue;
const UserScriptIDPair& id_pair = iter->second;
ids_to_delete.insert(id_pair.id);
scripts_to_delete.insert(id_pair);
map.erase(iter);
}
}
// Step 2: makes WebViewContentScriptManager become an observer of the
// |loader| for scripts loaded event.
UserScriptLoader* loader = master->loader();
DCHECK(loader);
if (!user_script_loader_observer_.IsObserving(loader))
user_script_loader_observer_.Add(loader);
// Step 3: removes content scripts from master.
master->RemoveScripts(scripts_to_delete);
// Step 4: updates WebViewRenderState in the IO thread.
if (!ids_to_delete.empty()) {
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::IO},
base::Bind(&WebViewRendererState::RemoveContentScriptIDs,
base::Unretained(WebViewRendererState::GetInstance()),
embedder_process_id, view_instance_id, ids_to_delete));
}
}
std::set<int> WebViewContentScriptManager::GetContentScriptIDSet(
int embedder_process_id,
int view_instance_id) {
std::set<int> ids;
GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
GuestContentScriptMap::const_iterator iter =
guest_content_script_map_.find(key);
if (iter == guest_content_script_map_.end())
return ids;
const ContentScriptMap& map = iter->second;
for (const auto& id_pair : map)
ids.insert(id_pair.second.id);
return ids;
}
void WebViewContentScriptManager::SignalOnScriptsLoaded(
base::OnceClosure callback) {
if (!user_script_loader_observer_.IsObservingSources()) {
std::move(callback).Run();
return;
}
pending_scripts_loading_callbacks_.push_back(std::move(callback));
}
void WebViewContentScriptManager::OnScriptsLoaded(UserScriptLoader* loader) {
user_script_loader_observer_.Remove(loader);
RunCallbacksIfReady();
}
void WebViewContentScriptManager::OnUserScriptLoaderDestroyed(
UserScriptLoader* loader) {
user_script_loader_observer_.Remove(loader);
RunCallbacksIfReady();
}
void WebViewContentScriptManager::RunCallbacksIfReady() {
if (user_script_loader_observer_.IsObservingSources())
return;
for (auto& callback : pending_scripts_loading_callbacks_)
std::move(callback).Run();
pending_scripts_loading_callbacks_.clear();
}
} // namespace extensions