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
{
34 OwnerWebContentsObserver(content::WebContents
* embedder_web_contents
,
35 const HostID
& host_id
,
36 WebViewContentScriptManager
* manager
)
37 : WebContentsObserver(embedder_web_contents
),
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
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
59 RemoveContentScripts();
62 void add_view_instance_id(int view_instance_id
) {
63 view_instance_ids_
.insert(view_instance_id
);
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());
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
));
103 manager
= new WebViewContentScriptManager(browser_context
);
104 browser_context
->SetUserData(webview::kWebViewContentScriptManagerKeyName
,
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
);
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
136 if (iter
== guest_content_script_map_
.end()) {
137 iter
= guest_content_script_map_
.insert(
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
);
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();
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.
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
;
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())
212 DeclarativeUserScriptMaster
* master
=
213 ExtensionSystem::Get(browser_context_
)
214 ->declarative_user_script_manager()
215 ->GetDeclarativeUserScriptMasterByID(host_id
);
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());
236 for (const std::string
& name
: script_name_list
) {
237 ContentScriptMap::iterator iter
= map
.find(name
);
238 if (iter
== map
.end())
240 const UserScript
& script
= iter
->second
;
241 ids_to_delete
.insert(script
.id());
242 scripts_to_delete
.insert(script
);
247 // Step 2: makes WebViewContentScriptManager become an observer of the
248 // |loader| for scripts loaded event.
249 UserScriptLoader
* loader
= master
->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
) {
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())
282 const ContentScriptMap
& map
= iter
->second
;
283 for (const auto& pair
: map
)
284 ids
.insert(pair
.second
.id());
289 void WebViewContentScriptManager::SignalOnScriptsLoaded(
290 const base::Closure
& callback
) {
291 if (!user_script_loader_observer_
.IsObservingSources()) {
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())
312 for (auto& callback
: pending_scripts_loading_callbacks_
)
314 pending_scripts_loading_callbacks_
.clear();
317 } // namespace extensions