OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager .h" | |
6 | |
7 #include "base/lazy_instance.h" | |
8 #include "base/memory/linked_ptr.h" | |
9 #include "content/public/browser/browser_context.h" | |
10 #include "content/public/browser/browser_thread.h" | |
11 #include "content/public/browser/navigation_details.h" | |
12 #include "content/public/browser/render_process_host.h" | |
13 #include "content/public/browser/web_contents.h" | |
14 #include "content/public/browser/web_contents_observer.h" | |
15 #include "extensions/browser/declarative_user_script_manager.h" | |
16 #include "extensions/browser/declarative_user_script_master.h" | |
17 #include "extensions/browser/extension_system.h" | |
18 #include "extensions/browser/guest_view/guest_view_manager.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" | |
22 | |
23 using content::BrowserThread; | |
24 | |
25 namespace { | |
26 bool RemoveContentScriptsForGuest(content::BrowserContext* browser_context, | |
27 HostID host_id, | |
28 content::WebContents* embedder_web_contents, | |
29 content::WebContents* guest_web_contents) { | |
30 auto guest = extensions::WebViewGuest::FromWebContents(guest_web_contents); | |
31 if (!guest) | |
32 return false; | |
33 | |
34 extensions::WebViewContentScriptManager* manager = | |
35 extensions::WebViewContentScriptManager::Get(browser_context); | |
36 if (!manager) | |
37 return false; | |
38 | |
39 manager->RemoveContentScripts(embedder_web_contents, | |
40 guest->view_instance_id(), host_id, | |
41 std::vector<std::string>()); | |
42 return true; | |
Fady Samuel
2015/04/10 16:16:04
I think you should return false here too to allow
Xi Han
2015/04/10 18:27:07
Done.
| |
43 } | |
44 } | |
45 | |
46 namespace extensions { | |
47 | |
48 // This observer ensures that the content scripts added by the guest are removed | |
49 // when its embedder goes away. | |
50 class WebViewContentScriptManager::OwnerWebContentsObserver | |
51 : public content::WebContentsObserver { | |
52 public: | |
53 OwnerWebContentsObserver(content::WebContents* embedder_web_contents, | |
54 const HostID& host_id, | |
55 WebViewContentScriptManager* manager) | |
56 : WebContentsObserver(embedder_web_contents), | |
57 host_id_(host_id), | |
58 web_view_content_script_manager_(manager) {} | |
59 ~OwnerWebContentsObserver() override {} | |
60 | |
61 // WebContentsObserver: | |
62 void WebContentsDestroyed() override { | |
63 // If the embedder is destroyed then remove all the content scripts of the | |
64 // guest. | |
65 RemoveContentScripts(); | |
66 } | |
67 void DidNavigateMainFrame( | |
68 const content::LoadCommittedDetails& details, | |
69 const content::FrameNavigateParams& params) override { | |
70 // If the embedder navigates to a different page then destroy the guest. | |
71 if (details.is_navigation_to_different_page()) | |
72 RemoveContentScripts(); | |
73 } | |
74 void RenderProcessGone(base::TerminationStatus status) override { | |
75 // If the embedder crashes, then destroy the guest. | |
76 RemoveContentScripts(); | |
77 } | |
78 | |
79 private: | |
80 void RemoveContentScripts() { | |
81 web_view_content_script_manager_->RemoveContentScripts(web_contents(), | |
82 host_id_); | |
83 } | |
84 | |
85 HostID host_id_; | |
86 WebViewContentScriptManager* web_view_content_script_manager_; | |
87 | |
88 DISALLOW_COPY_AND_ASSIGN(OwnerWebContentsObserver); | |
89 }; | |
90 | |
91 WebViewContentScriptManager::WebViewContentScriptManager( | |
92 content::BrowserContext* browser_context) | |
93 : browser_context_(browser_context) { | |
94 } | |
95 | |
96 WebViewContentScriptManager::~WebViewContentScriptManager() { | |
97 } | |
98 | |
99 WebViewContentScriptManager* WebViewContentScriptManager::Get( | |
100 content::BrowserContext* browser_context) { | |
101 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
102 WebViewContentScriptManager* manager = | |
103 static_cast<WebViewContentScriptManager*>(browser_context->GetUserData( | |
104 webview::kWebViewContentScriptManagerKeyName)); | |
105 if (!manager) { | |
106 manager = new WebViewContentScriptManager(browser_context); | |
107 browser_context->SetUserData(webview::kWebViewContentScriptManagerKeyName, | |
108 manager); | |
109 } | |
110 return manager; | |
111 } | |
112 | |
113 void WebViewContentScriptManager::AddContentScripts( | |
114 content::WebContents* embedder_web_contents, | |
115 int view_instance_id, | |
116 const HostID& host_id, | |
117 const std::map<std::string, UserScript>& scripts) { | |
118 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
119 DCHECK(embedder_web_contents); | |
120 | |
121 DeclarativeUserScriptMaster* master = | |
122 ExtensionSystem::Get(browser_context_) | |
123 ->declarative_user_script_manager() | |
124 ->GetDeclarativeUserScriptMasterByID(host_id); | |
125 DCHECK(master); | |
126 | |
127 // We need to update WebViewRenderState in the IO thread if the guest exists. | |
128 std::set<int> ids_to_add; | |
129 | |
130 int embedder_process_id = | |
131 embedder_web_contents->GetRenderProcessHost()->GetID(); | |
132 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id); | |
133 GuestContentScriptMap::iterator iter = guest_content_script_map_.find(key); | |
134 | |
135 // Step 1: finds the entry in guest_content_script_map_ by the given |key|. | |
136 // If there isn't any content script added for the given guest yet, insert an | |
137 // empty map first. | |
138 if (iter == guest_content_script_map_.end()) { | |
139 iter = guest_content_script_map_.insert( | |
140 iter, std::pair<GuestMapKey, ContentScriptMap>( | |
141 key, std::map<std::string, UserScript>())); | |
142 } | |
143 | |
144 // Step 2: updates the guest_content_script_map_, and adds content scripts to | |
145 // the master. | |
146 ContentScriptMap& map = iter->second; | |
147 for (const std::pair<std::string, UserScript>& pair : scripts) { | |
148 auto map_iter = map.find(pair.first); | |
149 // If a content script has the same name as the new one, removes the old | |
150 // script first, and insert the new one. | |
151 if (map_iter != map.end()) { | |
152 master->RemoveScript(map_iter->second); | |
Devlin
2015/04/10 16:04:32
What happens when a WebUI has two webviews, and ad
Fady Samuel
2015/04/10 16:16:04
This API doesn't allow two webviews to share a con
Xi Han
2015/04/10 18:27:07
We will create two different user scripts for them
| |
153 map.erase(map_iter); | |
154 } | |
155 map.insert(pair); | |
156 ids_to_add.insert(pair.second.id()); | |
157 master->AddScript(pair.second); | |
158 } | |
159 | |
160 // Step 3: creates owner web contents observer for the given | |
161 // |embedder_web_contents| if it doesn't exist. | |
162 auto observers_map_iter = | |
163 owner_web_contents_observer_map_.find(embedder_web_contents); | |
164 if (observers_map_iter == owner_web_contents_observer_map_.end()) { | |
165 linked_ptr<OwnerWebContentsObserver> observer( | |
166 new OwnerWebContentsObserver(embedder_web_contents, host_id, this)); | |
167 owner_web_contents_observer_map_[embedder_web_contents] = observer; | |
168 } | |
169 | |
170 // Step 4: updates WebViewRenderState in the IO thread. | |
171 if (!ids_to_add.empty()) { | |
172 content::BrowserThread::PostTask( | |
173 content::BrowserThread::IO, FROM_HERE, | |
174 base::Bind(&WebViewRendererState::AddContentScriptIDs, | |
175 base::Unretained(WebViewRendererState::GetInstance()), | |
176 embedder_process_id, view_instance_id, ids_to_add)); | |
177 } | |
178 } | |
179 | |
180 void WebViewContentScriptManager::RemoveContentScripts( | |
181 content::WebContents* embedder_web_contents, | |
182 int view_instance_id, | |
183 const HostID& host_id, | |
184 const std::vector<std::string>& script_name_list) { | |
185 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
186 | |
187 int embedder_process_id = | |
188 embedder_web_contents->GetRenderProcessHost()->GetID(); | |
189 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id); | |
190 GuestContentScriptMap::iterator script_map_iter = | |
191 guest_content_script_map_.find(key); | |
192 if (script_map_iter == guest_content_script_map_.end()) | |
193 return; | |
194 | |
195 DeclarativeUserScriptMaster* master = | |
196 ExtensionSystem::Get(browser_context_) | |
197 ->declarative_user_script_manager() | |
198 ->GetDeclarativeUserScriptMasterByID(host_id); | |
199 CHECK(master); | |
200 | |
201 // We need to update WebViewRenderState in the IO thread if the guest exists. | |
202 std::set<int> ids_to_delete; | |
203 | |
204 std::vector<std::string> names_to_delete; | |
205 | |
206 // Step 1: removed content scripts from |master| and updates | |
207 // |guest_content_script_map_| | |
208 std::map<std::string, UserScript>& map = script_map_iter->second; | |
209 // If the |script_name_list| is empty, all the content scripts added by the | |
210 // guest will be removed; otherwise, removes the scripts in the | |
211 // |script_name_list|. | |
212 if (script_name_list.empty()) { | |
213 for (const std::pair<std::string, UserScript>& iter : map) | |
214 names_to_delete.push_back(iter.first); | |
215 } else { | |
216 names_to_delete = script_name_list; | |
217 } | |
218 | |
219 for (const std::string& name : names_to_delete) { | |
220 ContentScriptMap::iterator iter = map.find(name); | |
221 if (iter == map.end()) | |
222 continue; | |
223 const UserScript& script = iter->second; | |
224 ids_to_delete.insert(script.id()); | |
225 master->RemoveScript(script); | |
226 map.erase(iter); | |
227 } | |
228 | |
229 // Step 2: Update WebViewRenderState in the IO thread. | |
230 if (!ids_to_delete.empty()) { | |
231 content::BrowserThread::PostTask( | |
232 content::BrowserThread::IO, FROM_HERE, | |
233 base::Bind(&WebViewRendererState::RemoveContentScriptIDs, | |
234 base::Unretained(WebViewRendererState::GetInstance()), | |
235 embedder_process_id, view_instance_id, ids_to_delete)); | |
236 } | |
237 } | |
238 | |
239 void WebViewContentScriptManager::RemoveContentScripts( | |
240 content::WebContents* embedder_web_contents, | |
241 const HostID& host_id) { | |
242 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
243 | |
244 // Step 1: removes content scripts of all the guests embedded. | |
245 auto guest_view_manager = | |
246 GuestViewManager::FromBrowserContext(browser_context_); | |
247 guest_view_manager->ForEachGuest( | |
248 embedder_web_contents, | |
249 base::Bind(&RemoveContentScriptsForGuest, browser_context_, host_id, | |
250 embedder_web_contents)); | |
251 | |
252 // Step 2: removes the observer of the given |embedder_web_contents|. | |
253 owner_web_contents_observer_map_.erase(embedder_web_contents); | |
254 } | |
255 | |
256 std::set<int> WebViewContentScriptManager::GetContentScriptIDSet( | |
257 int embedder_process_id, | |
258 int view_instance_id) { | |
259 std::set<int> ids; | |
260 | |
261 GuestMapKey key = std::pair<int, int>(embedder_process_id, view_instance_id); | |
262 GuestContentScriptMap::const_iterator iter = | |
263 guest_content_script_map_.find(key); | |
264 if (iter == guest_content_script_map_.end()) | |
265 return ids; | |
266 const ContentScriptMap& map = iter->second; | |
267 for (const auto& pair : map) | |
268 ids.insert(pair.second.id()); | |
269 | |
270 return ids; | |
271 } | |
272 | |
273 } // namespace extensions | |
OLD | NEW |