diff options
Diffstat (limited to 'chromium/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc')
-rw-r--r-- | chromium/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/chromium/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc b/chromium/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc new file mode 100644 index 00000000000..8a5592bddb5 --- /dev/null +++ b/chromium/extensions/browser/guest_view/web_view/web_view_content_script_manager.cc @@ -0,0 +1,251 @@ +// 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 "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_details.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_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, + manager); + } + return manager; +} + +void WebViewContentScriptManager::AddContentScripts( + int embedder_process_id, + content::RenderViewHost* render_view_host, + int view_instance_id, + const HostID& host_id, + const std::set<UserScript>& 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); + GuestContentScriptMap::iterator 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<UserScript> scripts_to_delete; + for (const 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()) { + scripts_to_delete.insert(map_iter->second); + map.erase(map_iter); + } + map.insert(std::pair<std::string, UserScript>(script.name(), script)); + ids_to_add.insert(script.id()); + } + + if (!scripts_to_delete.empty()) { + master->RemoveScripts(scripts_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(scripts, embedder_process_id, + render_view_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()) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + 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); + GuestContentScriptMap::iterator 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<UserScript> scripts_to_delete; + + // Step 1: removes content scripts from |master| and updates + // |guest_content_script_map_|. + std::map<std::string, UserScript>& 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) { + ContentScriptMap::iterator iter = map.find(name); + if (iter == map.end()) + continue; + const UserScript& script = iter->second; + ids_to_delete.insert(script.id()); + scripts_to_delete.insert(script); + 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()) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + 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& pair : map) + ids.insert(pair.second.id()); + + return ids; +} + +void WebViewContentScriptManager::SignalOnScriptsLoaded( + const base::Closure& callback) { + if (!user_script_loader_observer_.IsObservingSources()) { + callback.Run(); + return; + } + pending_scripts_loading_callbacks_.push_back(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_) + callback.Run(); + pending_scripts_loading_callbacks_.clear(); +} + +} // namespace extensions |