1 // Copyright 2014 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/guest_view_manager.h" 6 7 #include "base/strings/stringprintf.h" 8 #include "content/public/browser/browser_context.h" 9 #include "content/public/browser/render_process_host.h" 10 #include "content/public/browser/render_view_host.h" 11 #include "content/public/browser/user_metrics.h" 12 #include "content/public/browser/web_contents_observer.h" 13 #include "content/public/common/result_codes.h" 14 #include "content/public/common/url_constants.h" 15 #include "extensions/browser/extension_system.h" 16 #include "extensions/browser/guest_view/guest_view_base.h" 17 #include "extensions/browser/guest_view/guest_view_manager_factory.h" 18 #include "extensions/common/guest_view/guest_view_constants.h" 19 #include "net/base/escape.h" 20 #include "url/gurl.h" 21 22 using content::BrowserContext; 23 using content::SiteInstance; 24 using content::WebContents; 25 26 namespace extensions { 27 28 // static 29 GuestViewManagerFactory* GuestViewManager::factory_ = NULL; 30 31 GuestViewManager::GuestViewManager(content::BrowserContext* context) 32 : current_instance_id_(0), last_instance_id_removed_(0), context_(context) { 33 } 34 35 GuestViewManager::~GuestViewManager() {} 36 37 // static. 38 GuestViewManager* GuestViewManager::FromBrowserContext( 39 BrowserContext* context) { 40 GuestViewManager* guest_manager = 41 static_cast<GuestViewManager*>(context->GetUserData( 42 guestview::kGuestViewManagerKeyName)); 43 if (!guest_manager) { 44 if (factory_) { 45 guest_manager = factory_->CreateGuestViewManager(context); 46 } else { 47 guest_manager = new GuestViewManager(context); 48 } 49 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager); 50 } 51 return guest_manager; 52 } 53 54 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely( 55 int guest_instance_id, 56 int embedder_render_process_id) { 57 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id, 58 guest_instance_id)) { 59 return NULL; 60 } 61 return GetGuestByInstanceID(guest_instance_id); 62 } 63 64 void GuestViewManager::AttachGuest( 65 int embedder_render_process_id, 66 int embedder_routing_id, 67 int element_instance_id, 68 int guest_instance_id, 69 const base::DictionaryValue& attach_params) { 70 content::WebContents* guest_web_contents = 71 GetGuestByInstanceIDSafely(guest_instance_id, embedder_render_process_id); 72 if (!guest_web_contents) 73 return; 74 75 GuestViewBase* guest_view = 76 GuestViewBase::FromWebContents(guest_web_contents); 77 DCHECK(guest_view); 78 79 content::RenderViewHost* rvh = 80 content::RenderViewHost::FromID(embedder_render_process_id, 81 embedder_routing_id); 82 content::WebContents* embedder_web_contents = 83 content::WebContents::FromRenderViewHost(rvh); 84 if (!embedder_web_contents) 85 return; 86 ElementInstanceKey key(embedder_web_contents, element_instance_id); 87 88 GuestInstanceIDMap::iterator it = instance_id_map_.find(key); 89 if (it != instance_id_map_.end()) { 90 int old_guest_instance_id = it->second; 91 // Reattachment to the same guest is not currently supported. 92 if (old_guest_instance_id == guest_instance_id) 93 return; 94 95 content::WebContents* old_guest_web_contents = 96 GetGuestByInstanceIDSafely(old_guest_instance_id, 97 embedder_render_process_id); 98 if (!old_guest_web_contents) 99 return; 100 101 GuestViewBase* old_guest_view = 102 GuestViewBase::FromWebContents(old_guest_web_contents); 103 104 old_guest_view->Destroy(); 105 } 106 instance_id_map_[key] = guest_instance_id; 107 reverse_instance_id_map_.insert(std::make_pair(guest_instance_id, key)); 108 guest_view->SetAttachParams(attach_params); 109 } 110 111 int GuestViewManager::GetNextInstanceID() { 112 return ++current_instance_id_; 113 } 114 115 void GuestViewManager::CreateGuest(const std::string& view_type, 116 const std::string& embedder_extension_id, 117 content::WebContents* embedder_web_contents, 118 const base::DictionaryValue& create_params, 119 const WebContentsCreatedCallback& callback) { 120 int guest_instance_id = GetNextInstanceID(); 121 GuestViewBase* guest = 122 GuestViewBase::Create(context_, guest_instance_id, view_type); 123 if (!guest) { 124 callback.Run(NULL); 125 return; 126 } 127 guest->Init( 128 embedder_extension_id, embedder_web_contents, create_params, callback); 129 } 130 131 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams( 132 const std::string& view_type, 133 const std::string& embedder_extension_id, 134 int embedder_render_process_id, 135 const content::WebContents::CreateParams& create_params) { 136 int guest_instance_id = GetNextInstanceID(); 137 GuestViewBase* guest = 138 GuestViewBase::Create(context_, guest_instance_id, view_type); 139 if (!guest) 140 return NULL; 141 content::WebContents::CreateParams guest_create_params(create_params); 142 guest_create_params.guest_delegate = guest; 143 content::WebContents* guest_web_contents = 144 WebContents::Create(guest_create_params); 145 guest->InitWithWebContents(embedder_extension_id, 146 embedder_render_process_id, 147 guest_web_contents); 148 return guest_web_contents; 149 } 150 151 content::WebContents* GuestViewManager::GetGuestByInstanceID( 152 content::WebContents* embedder_web_contents, 153 int element_instance_id) { 154 int guest_instance_id = GetGuestInstanceIDForElementID(embedder_web_contents, 155 element_instance_id); 156 if (guest_instance_id == guestview::kInstanceIDNone) 157 return NULL; 158 159 return GetGuestByInstanceID(guest_instance_id); 160 } 161 162 int GuestViewManager::GetGuestInstanceIDForElementID( 163 content::WebContents* embedder_web_contents, 164 int element_instance_id) { 165 GuestInstanceIDMap::iterator iter = instance_id_map_.find( 166 ElementInstanceKey(embedder_web_contents, element_instance_id)); 167 if (iter == instance_id_map_.end()) 168 return guestview::kInstanceIDNone; 169 return iter->second; 170 } 171 172 SiteInstance* GuestViewManager::GetGuestSiteInstance( 173 const GURL& guest_site) { 174 for (GuestInstanceMap::const_iterator it = 175 guest_web_contents_by_instance_id_.begin(); 176 it != guest_web_contents_by_instance_id_.end(); ++it) { 177 if (it->second->GetSiteInstance()->GetSiteURL() == guest_site) 178 return it->second->GetSiteInstance(); 179 } 180 return NULL; 181 } 182 183 bool GuestViewManager::ForEachGuest(WebContents* embedder_web_contents, 184 const GuestCallback& callback) { 185 for (GuestInstanceMap::iterator it = 186 guest_web_contents_by_instance_id_.begin(); 187 it != guest_web_contents_by_instance_id_.end(); ++it) { 188 WebContents* guest = it->second; 189 GuestViewBase* guest_view = GuestViewBase::FromWebContents(guest); 190 if (embedder_web_contents != guest_view->embedder_web_contents()) 191 continue; 192 193 if (callback.Run(guest)) 194 return true; 195 } 196 return false; 197 } 198 199 void GuestViewManager::AddGuest(int guest_instance_id, 200 WebContents* guest_web_contents) { 201 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id)); 202 CHECK(CanUseGuestInstanceID(guest_instance_id)); 203 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents; 204 } 205 206 void GuestViewManager::RemoveGuest(int guest_instance_id) { 207 GuestInstanceMap::iterator it = 208 guest_web_contents_by_instance_id_.find(guest_instance_id); 209 DCHECK(it != guest_web_contents_by_instance_id_.end()); 210 guest_web_contents_by_instance_id_.erase(it); 211 212 GuestInstanceIDReverseMap::iterator id_iter = 213 reverse_instance_id_map_.find(guest_instance_id); 214 if (id_iter != reverse_instance_id_map_.end()) { 215 const ElementInstanceKey& instance_id_key = id_iter->second; 216 instance_id_map_.erase(instance_id_map_.find(instance_id_key)); 217 reverse_instance_id_map_.erase(id_iter); 218 } 219 220 // All the instance IDs that lie within [0, last_instance_id_removed_] 221 // are invalid. 222 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set. 223 // The following code compacts the set by incrementing 224 // |last_instance_id_removed_|. 225 if (guest_instance_id == last_instance_id_removed_ + 1) { 226 ++last_instance_id_removed_; 227 // Compact. 228 std::set<int>::iterator iter = removed_instance_ids_.begin(); 229 while (iter != removed_instance_ids_.end()) { 230 int instance_id = *iter; 231 // The sparse invalid IDs must not lie within 232 // [0, last_instance_id_removed_] 233 DCHECK(instance_id > last_instance_id_removed_); 234 if (instance_id != last_instance_id_removed_ + 1) 235 break; 236 ++last_instance_id_removed_; 237 removed_instance_ids_.erase(iter++); 238 } 239 } else { 240 removed_instance_ids_.insert(guest_instance_id); 241 } 242 } 243 244 content::WebContents* GuestViewManager::GetGuestByInstanceID( 245 int guest_instance_id) { 246 GuestInstanceMap::const_iterator it = 247 guest_web_contents_by_instance_id_.find(guest_instance_id); 248 if (it == guest_web_contents_by_instance_id_.end()) 249 return NULL; 250 return it->second; 251 } 252 253 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill( 254 int embedder_render_process_id, 255 int guest_instance_id) { 256 if (!CanEmbedderAccessInstanceID(embedder_render_process_id, 257 guest_instance_id)) { 258 // The embedder process is trying to access a guest it does not own. 259 content::RecordAction( 260 base::UserMetricsAction("BadMessageTerminate_BPGM")); 261 base::KillProcess( 262 content::RenderProcessHost::FromID(embedder_render_process_id)-> 263 GetHandle(), 264 content::RESULT_CODE_KILLED_BAD_MESSAGE, false); 265 return false; 266 } 267 return true; 268 } 269 270 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) { 271 if (guest_instance_id <= last_instance_id_removed_) 272 return false; 273 return !ContainsKey(removed_instance_ids_, guest_instance_id); 274 } 275 276 bool GuestViewManager::CanEmbedderAccessInstanceID( 277 int embedder_render_process_id, 278 int guest_instance_id) { 279 // The embedder is trying to access a guest with a negative or zero 280 // instance ID. 281 if (guest_instance_id <= guestview::kInstanceIDNone) 282 return false; 283 284 // The embedder is trying to access an instance ID that has not yet been 285 // allocated by GuestViewManager. This could cause instance ID 286 // collisions in the future, and potentially give one embedder access to a 287 // guest it does not own. 288 if (guest_instance_id > current_instance_id_) 289 return false; 290 291 // We might get some late arriving messages at tear down. Let's let the 292 // embedder tear down in peace. 293 GuestInstanceMap::const_iterator it = 294 guest_web_contents_by_instance_id_.find(guest_instance_id); 295 if (it == guest_web_contents_by_instance_id_.end()) 296 return true; 297 298 GuestViewBase* guest_view = GuestViewBase::FromWebContents(it->second); 299 if (!guest_view) 300 return false; 301 302 return embedder_render_process_id == guest_view->embedder_render_process_id(); 303 } 304 305 } // namespace extensions 306