Add callback in UserScriptLoader to notify users when scripts are loaded.
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_content_script_manager.cc
blobb03eab7a8e64d9b043cb6f9a459fdf32de4f7b8a
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.
5 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
7 #include "base/lazy_instance.h"
8 #include "base/memory/linked_ptr.h"
9 #include "components/guest_view/browser/guest_view_manager.h"
10 #include "content/public/browser/browser_context.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/browser/web_contents_observer.h"
16 #include "extensions/browser/declarative_user_script_manager.h"
17 #include "extensions/browser/declarative_user_script_master.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
20 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
21 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
23 using content::BrowserThread;
25 namespace extensions {
27 // This observer ensures that the content scripts added by the guest are removed
28 // when its embedder goes away.
29 // The OwnerWebContentsObserver object will be destroyed when the embedder web
30 // contents it observed is gone.
31 class WebViewContentScriptManager::OwnerWebContentsObserver
32 : public content::WebContentsObserver {
33 public:
34 OwnerWebContentsObserver(content::WebContents* embedder_web_contents,
35 const HostID& host_id,
36 WebViewContentScriptManager* manager)
37 : WebContentsObserver(embedder_web_contents),
38 host_id_(host_id),
39 web_view_content_script_manager_(manager) {}
40 ~OwnerWebContentsObserver() override {}
42 // WebContentsObserver:
43 void WebContentsDestroyed() override {
44 // If the embedder is destroyed then remove all the content scripts of the
45 // guest.
46 RemoveContentScripts();
48 void DidNavigateMainFrame(
49 const content::LoadCommittedDetails& details,
50 const content::FrameNavigateParams& params) override {
51 // If the embedder navigates to a different page then remove all the content
52 // scripts of the guest.
53 if (details.is_navigation_to_different_page())
54 RemoveContentScripts();
56 void RenderProcessGone(base::TerminationStatus status) override {
57 // If the embedder crashes, then remove all the content scripts of the
58 // guest.
59 RemoveContentScripts();
62 void add_view_instance_id(int view_instance_id) {
63 view_instance_ids_.insert(view_instance_id);
66 private:
67 void RemoveContentScripts() {
68 DCHECK(web_view_content_script_manager_);
70 // Step 1: removes content scripts of all the guests embedded.
71 for (int view_instance_id : view_instance_ids_) {
72 web_view_content_script_manager_->RemoveContentScripts(
73 web_contents(), view_instance_id, host_id_,
74 std::vector<std::string>());
76 // Step 2: removes this observer.
77 // This object can be deleted after this line.
78 web_view_content_script_manager_->RemoveObserver(web_contents());
81 HostID host_id_;
82 std::set<int> view_instance_ids_;
83 WebViewContentScriptManager* web_view_content_script_manager_;
85 DISALLOW_COPY_AND_ASSIGN(OwnerWebContentsObserver);
88 WebViewContentScriptManager::WebViewContentScriptManager(
89 content::BrowserContext* browser_context)
90 : user_script_loader_observer_(this), browser_context_(browser_context) {
93 WebViewContentScriptManager::~WebViewContentScriptManager() {
96 WebViewContentScriptManager* WebViewContentScriptManager::Get(
97 content::BrowserContext* browser_context) {
98 DCHECK_CURRENTLY_ON(BrowserThread::UI);
99 WebViewContentScriptManager* manager =
100 static_cast<WebViewContentScriptManager*>(browser_context->GetUserData(
101 webview::kWebViewContentScriptManagerKeyName));
102 if (!manager) {
103 manager = new WebViewContentScriptManager(browser_context);
104 browser_context->SetUserData(webview::kWebViewContentScriptManagerKeyName,
105 manager);
107 return manager;
110 void WebViewContentScriptManager::AddContentScripts(
111 content::WebContents* embedder_web_contents,
112 int embedder_routing_id,
113 int view_instance_id,
114 const HostID& host_id,
115 const std::set<UserScript>& scripts) {
116 DCHECK_CURRENTLY_ON(BrowserThread::UI);
117 DCHECK(embedder_web_contents);
119 DeclarativeUserScriptMaster* master =
120 ExtensionSystem::Get(browser_context_)
121 ->declarative_user_script_manager()
122 ->GetDeclarativeUserScriptMasterByID(host_id);
123 DCHECK(master);
125 // We need to update WebViewRenderState in the IO thread if the guest exists.
126 std::set<int> ids_to_add;
128 int embedder_process_id =
129 embedder_web_contents->GetRenderProcessHost()->GetID();
130 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
131 GuestContentScriptMap::iterator iter = guest_content_script_map_.find(key);
133 // Step 1: finds the entry in guest_content_script_map_ by the given |key|.
134 // If there isn't any content script added for the given guest yet, insert an
135 // empty map first.
136 if (iter == guest_content_script_map_.end()) {
137 iter = guest_content_script_map_.insert(
138 iter,
139 std::pair<GuestMapKey, ContentScriptMap>(key, ContentScriptMap()));
142 // Step 2: updates the guest_content_script_map_.
143 ContentScriptMap& map = iter->second;
144 std::set<UserScript> scripts_to_delete;
145 for (const UserScript& script : scripts) {
146 auto map_iter = map.find(script.name());
147 // If a content script has the same name as the new one, remove the old
148 // script first, and insert the new one.
149 if (map_iter != map.end()) {
150 scripts_to_delete.insert(map_iter->second);
151 map.erase(map_iter);
153 map.insert(std::pair<std::string, UserScript>(script.name(), script));
154 ids_to_add.insert(script.id());
157 if (!scripts_to_delete.empty()) {
158 master->RemoveScripts(scripts_to_delete);
161 // Step 3: makes WebViewContentScriptManager become an observer of the
162 // |loader| for scripts loaded event.
163 UserScriptLoader* loader = master->loader();
164 DCHECK(loader);
165 if (!user_script_loader_observer_.IsObserving(loader))
166 user_script_loader_observer_.Add(loader);
168 // Step 4: adds new scripts to the master.
169 master->AddScripts(scripts, embedder_process_id, embedder_routing_id);
171 // Step 5: creates owner web contents observer for the given
172 // |embedder_web_contents| if it doesn't exist.
173 auto observer_iter =
174 owner_web_contents_observer_map_.find(embedder_web_contents);
175 if (observer_iter == owner_web_contents_observer_map_.end()) {
176 linked_ptr<OwnerWebContentsObserver> observer(
177 new OwnerWebContentsObserver(embedder_web_contents, host_id, this));
178 observer->add_view_instance_id(view_instance_id);
179 owner_web_contents_observer_map_[embedder_web_contents] = observer;
180 } else {
181 observer_iter->second->add_view_instance_id(view_instance_id);
184 // Step 6: updates WebViewRenderState in the IO thread.
185 // It is safe to use base::Unretained(WebViewRendererState::GetInstance())
186 // since WebViewRendererState::GetInstance() always returns a Singleton of
187 // WebViewRendererState.
188 if (!ids_to_add.empty()) {
189 content::BrowserThread::PostTask(
190 content::BrowserThread::IO, FROM_HERE,
191 base::Bind(&WebViewRendererState::AddContentScriptIDs,
192 base::Unretained(WebViewRendererState::GetInstance()),
193 embedder_process_id, view_instance_id, ids_to_add));
197 void WebViewContentScriptManager::RemoveContentScripts(
198 content::WebContents* embedder_web_contents,
199 int view_instance_id,
200 const HostID& host_id,
201 const std::vector<std::string>& script_name_list) {
202 DCHECK_CURRENTLY_ON(BrowserThread::UI);
204 int embedder_process_id =
205 embedder_web_contents->GetRenderProcessHost()->GetID();
206 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
207 GuestContentScriptMap::iterator script_map_iter =
208 guest_content_script_map_.find(key);
209 if (script_map_iter == guest_content_script_map_.end())
210 return;
212 DeclarativeUserScriptMaster* master =
213 ExtensionSystem::Get(browser_context_)
214 ->declarative_user_script_manager()
215 ->GetDeclarativeUserScriptMasterByID(host_id);
216 CHECK(master);
218 // We need to update WebViewRenderState in the IO thread if the guest exists.
219 std::set<int> ids_to_delete;
220 std::set<UserScript> scripts_to_delete;
222 // Step 1: removes content scripts from |master| and updates
223 // |guest_content_script_map_|.
224 std::map<std::string, UserScript>& map = script_map_iter->second;
225 // If the |script_name_list| is empty, all the content scripts added by the
226 // guest will be removed; otherwise, removes the scripts in the
227 // |script_name_list|.
228 if (script_name_list.empty()) {
229 auto it = map.begin();
230 while (it != map.end()) {
231 scripts_to_delete.insert(it->second);
232 ids_to_delete.insert(it->second.id());
233 map.erase(it++);
235 } else {
236 for (const std::string& name : script_name_list) {
237 ContentScriptMap::iterator iter = map.find(name);
238 if (iter == map.end())
239 continue;
240 const UserScript& script = iter->second;
241 ids_to_delete.insert(script.id());
242 scripts_to_delete.insert(script);
243 map.erase(iter);
247 // Step 2: makes WebViewContentScriptManager become an observer of the
248 // |loader| for scripts loaded event.
249 UserScriptLoader* loader = master->loader();
250 DCHECK(loader);
251 if (!user_script_loader_observer_.IsObserving(loader))
252 user_script_loader_observer_.Add(loader);
254 // Step 3: removes content scripts from master.
255 master->RemoveScripts(scripts_to_delete);
257 // Step 4: updates WebViewRenderState in the IO thread.
258 if (!ids_to_delete.empty()) {
259 content::BrowserThread::PostTask(
260 content::BrowserThread::IO, FROM_HERE,
261 base::Bind(&WebViewRendererState::RemoveContentScriptIDs,
262 base::Unretained(WebViewRendererState::GetInstance()),
263 embedder_process_id, view_instance_id, ids_to_delete));
267 void WebViewContentScriptManager::RemoveObserver(
268 content::WebContents* embedder_web_contents) {
269 owner_web_contents_observer_map_.erase(embedder_web_contents);
272 std::set<int> WebViewContentScriptManager::GetContentScriptIDSet(
273 int embedder_process_id,
274 int view_instance_id) {
275 std::set<int> ids;
277 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id);
278 GuestContentScriptMap::const_iterator iter =
279 guest_content_script_map_.find(key);
280 if (iter == guest_content_script_map_.end())
281 return ids;
282 const ContentScriptMap& map = iter->second;
283 for (const auto& pair : map)
284 ids.insert(pair.second.id());
286 return ids;
289 void WebViewContentScriptManager::SignalOnScriptsLoaded(
290 const base::Closure& callback) {
291 if (!user_script_loader_observer_.IsObservingSources()) {
292 callback.Run();
293 return;
295 pending_scripts_loading_callbacks_.push_back(callback);
298 void WebViewContentScriptManager::OnScriptsLoaded(UserScriptLoader* loader) {
299 user_script_loader_observer_.Remove(loader);
300 RunCallbacksIfReady();
303 void WebViewContentScriptManager::OnUserScriptLoaderDestroyed(
304 UserScriptLoader* loader) {
305 user_script_loader_observer_.Remove(loader);
306 RunCallbacksIfReady();
309 void WebViewContentScriptManager::RunCallbacksIfReady() {
310 if (user_script_loader_observer_.IsObservingSources())
311 return;
312 for (auto& callback : pending_scripts_loading_callbacks_)
313 callback.Run();
314 pending_scripts_loading_callbacks_.clear();
317 } // namespace extensions