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 DeclarativeUserScriptManager::Get(browser_context_
)
121 ->GetDeclarativeUserScriptMasterByID(host_id
);
124 // We need to update WebViewRenderState in the IO thread if the guest exists.
125 std::set
<int> ids_to_add
;
127 int embedder_process_id
=
128 embedder_web_contents
->GetRenderProcessHost()->GetID();
129 GuestMapKey key
= std::pair
<int, int>(embedder_process_id
, view_instance_id
);
130 GuestContentScriptMap::iterator iter
= guest_content_script_map_
.find(key
);
132 // Step 1: finds the entry in guest_content_script_map_ by the given |key|.
133 // If there isn't any content script added for the given guest yet, insert an
135 if (iter
== guest_content_script_map_
.end()) {
136 iter
= guest_content_script_map_
.insert(
138 std::pair
<GuestMapKey
, ContentScriptMap
>(key
, ContentScriptMap()));
141 // Step 2: updates the guest_content_script_map_.
142 ContentScriptMap
& map
= iter
->second
;
143 std::set
<UserScript
> scripts_to_delete
;
144 for (const UserScript
& script
: scripts
) {
145 auto map_iter
= map
.find(script
.name());
146 // If a content script has the same name as the new one, remove the old
147 // script first, and insert the new one.
148 if (map_iter
!= map
.end()) {
149 scripts_to_delete
.insert(map_iter
->second
);
152 map
.insert(std::pair
<std::string
, UserScript
>(script
.name(), script
));
153 ids_to_add
.insert(script
.id());
156 if (!scripts_to_delete
.empty()) {
157 master
->RemoveScripts(scripts_to_delete
);
160 // Step 3: makes WebViewContentScriptManager become an observer of the
161 // |loader| for scripts loaded event.
162 UserScriptLoader
* loader
= master
->loader();
164 if (!user_script_loader_observer_
.IsObserving(loader
))
165 user_script_loader_observer_
.Add(loader
);
167 // Step 4: adds new scripts to the master.
168 master
->AddScripts(scripts
, embedder_process_id
, embedder_routing_id
);
170 // Step 5: creates owner web contents observer for the given
171 // |embedder_web_contents| if it doesn't exist.
173 owner_web_contents_observer_map_
.find(embedder_web_contents
);
174 if (observer_iter
== owner_web_contents_observer_map_
.end()) {
175 linked_ptr
<OwnerWebContentsObserver
> observer(
176 new OwnerWebContentsObserver(embedder_web_contents
, host_id
, this));
177 observer
->add_view_instance_id(view_instance_id
);
178 owner_web_contents_observer_map_
[embedder_web_contents
] = observer
;
180 observer_iter
->second
->add_view_instance_id(view_instance_id
);
183 // Step 6: updates WebViewRenderState in the IO thread.
184 // It is safe to use base::Unretained(WebViewRendererState::GetInstance())
185 // since WebViewRendererState::GetInstance() always returns a Singleton of
186 // WebViewRendererState.
187 if (!ids_to_add
.empty()) {
188 content::BrowserThread::PostTask(
189 content::BrowserThread::IO
, FROM_HERE
,
190 base::Bind(&WebViewRendererState::AddContentScriptIDs
,
191 base::Unretained(WebViewRendererState::GetInstance()),
192 embedder_process_id
, view_instance_id
, ids_to_add
));
196 void WebViewContentScriptManager::RemoveContentScripts(
197 content::WebContents
* embedder_web_contents
,
198 int view_instance_id
,
199 const HostID
& host_id
,
200 const std::vector
<std::string
>& script_name_list
) {
201 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
203 int embedder_process_id
=
204 embedder_web_contents
->GetRenderProcessHost()->GetID();
205 GuestMapKey key
= std::pair
<int, int>(embedder_process_id
, view_instance_id
);
206 GuestContentScriptMap::iterator script_map_iter
=
207 guest_content_script_map_
.find(key
);
208 if (script_map_iter
== guest_content_script_map_
.end())
211 DeclarativeUserScriptMaster
* master
=
212 DeclarativeUserScriptManager::Get(browser_context_
)
213 ->GetDeclarativeUserScriptMasterByID(host_id
);
216 // We need to update WebViewRenderState in the IO thread if the guest exists.
217 std::set
<int> ids_to_delete
;
218 std::set
<UserScript
> scripts_to_delete
;
220 // Step 1: removes content scripts from |master| and updates
221 // |guest_content_script_map_|.
222 std::map
<std::string
, UserScript
>& map
= script_map_iter
->second
;
223 // If the |script_name_list| is empty, all the content scripts added by the
224 // guest will be removed; otherwise, removes the scripts in the
225 // |script_name_list|.
226 if (script_name_list
.empty()) {
227 auto it
= map
.begin();
228 while (it
!= map
.end()) {
229 scripts_to_delete
.insert(it
->second
);
230 ids_to_delete
.insert(it
->second
.id());
234 for (const std::string
& name
: script_name_list
) {
235 ContentScriptMap::iterator iter
= map
.find(name
);
236 if (iter
== map
.end())
238 const UserScript
& script
= iter
->second
;
239 ids_to_delete
.insert(script
.id());
240 scripts_to_delete
.insert(script
);
245 // Step 2: makes WebViewContentScriptManager become an observer of the
246 // |loader| for scripts loaded event.
247 UserScriptLoader
* loader
= master
->loader();
249 if (!user_script_loader_observer_
.IsObserving(loader
))
250 user_script_loader_observer_
.Add(loader
);
252 // Step 3: removes content scripts from master.
253 master
->RemoveScripts(scripts_to_delete
);
255 // Step 4: updates WebViewRenderState in the IO thread.
256 if (!ids_to_delete
.empty()) {
257 content::BrowserThread::PostTask(
258 content::BrowserThread::IO
, FROM_HERE
,
259 base::Bind(&WebViewRendererState::RemoveContentScriptIDs
,
260 base::Unretained(WebViewRendererState::GetInstance()),
261 embedder_process_id
, view_instance_id
, ids_to_delete
));
265 void WebViewContentScriptManager::RemoveObserver(
266 content::WebContents
* embedder_web_contents
) {
267 owner_web_contents_observer_map_
.erase(embedder_web_contents
);
270 std::set
<int> WebViewContentScriptManager::GetContentScriptIDSet(
271 int embedder_process_id
,
272 int view_instance_id
) {
275 GuestMapKey key
= std::pair
<int, int>(embedder_process_id
, view_instance_id
);
276 GuestContentScriptMap::const_iterator iter
=
277 guest_content_script_map_
.find(key
);
278 if (iter
== guest_content_script_map_
.end())
280 const ContentScriptMap
& map
= iter
->second
;
281 for (const auto& pair
: map
)
282 ids
.insert(pair
.second
.id());
287 void WebViewContentScriptManager::SignalOnScriptsLoaded(
288 const base::Closure
& callback
) {
289 if (!user_script_loader_observer_
.IsObservingSources()) {
293 pending_scripts_loading_callbacks_
.push_back(callback
);
296 void WebViewContentScriptManager::OnScriptsLoaded(UserScriptLoader
* loader
) {
297 user_script_loader_observer_
.Remove(loader
);
298 RunCallbacksIfReady();
301 void WebViewContentScriptManager::OnUserScriptLoaderDestroyed(
302 UserScriptLoader
* loader
) {
303 user_script_loader_observer_
.Remove(loader
);
304 RunCallbacksIfReady();
307 void WebViewContentScriptManager::RunCallbacksIfReady() {
308 if (user_script_loader_observer_
.IsObservingSources())
310 for (auto& callback
: pending_scripts_loading_callbacks_
)
312 pending_scripts_loading_callbacks_
.clear();
315 } // namespace extensions