| OLD | NEW |
| (Empty) |
| 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_frame_host.h" | |
| 10 #include "content/public/browser/render_process_host.h" | |
| 11 #include "content/public/browser/render_view_host.h" | |
| 12 #include "content/public/browser/user_metrics.h" | |
| 13 #include "content/public/browser/web_contents_observer.h" | |
| 14 #include "content/public/common/child_process_host.h" | |
| 15 #include "content/public/common/result_codes.h" | |
| 16 #include "content/public/common/url_constants.h" | |
| 17 #include "extensions/browser/guest_view/guest_view_base.h" | |
| 18 #include "extensions/browser/guest_view/guest_view_manager_delegate.h" | |
| 19 #include "extensions/browser/guest_view/guest_view_manager_factory.h" | |
| 20 #include "extensions/common/guest_view/guest_view_constants.h" | |
| 21 #include "net/base/escape.h" | |
| 22 #include "url/gurl.h" | |
| 23 | |
| 24 using content::BrowserContext; | |
| 25 using content::SiteInstance; | |
| 26 using content::WebContents; | |
| 27 using guestview::GuestViewManagerDelegate; | |
| 28 | |
| 29 namespace extensions { | |
| 30 | |
| 31 // static | |
| 32 GuestViewManagerFactory* GuestViewManager::factory_ = nullptr; | |
| 33 | |
| 34 GuestViewManager::GuestViewManager( | |
| 35 content::BrowserContext* context, | |
| 36 scoped_ptr<GuestViewManagerDelegate> delegate) | |
| 37 : current_instance_id_(0), | |
| 38 last_instance_id_removed_(0), | |
| 39 context_(context), | |
| 40 delegate_(delegate.Pass()) { | |
| 41 } | |
| 42 | |
| 43 GuestViewManager::~GuestViewManager() {} | |
| 44 | |
| 45 // static | |
| 46 GuestViewManager* GuestViewManager::CreateWithDelegate( | |
| 47 BrowserContext* context, | |
| 48 scoped_ptr<GuestViewManagerDelegate> delegate) { | |
| 49 GuestViewManager* guest_manager = FromBrowserContext(context); | |
| 50 if (!guest_manager) { | |
| 51 if (factory_) { | |
| 52 guest_manager = | |
| 53 factory_->CreateGuestViewManager(context, delegate.Pass()); | |
| 54 } else { | |
| 55 guest_manager = new GuestViewManager(context, delegate.Pass()); | |
| 56 } | |
| 57 context->SetUserData(guestview::kGuestViewManagerKeyName, guest_manager); | |
| 58 } | |
| 59 return guest_manager; | |
| 60 } | |
| 61 | |
| 62 // static | |
| 63 GuestViewManager* GuestViewManager::FromBrowserContext( | |
| 64 BrowserContext* context) { | |
| 65 return static_cast<GuestViewManager*>(context->GetUserData( | |
| 66 guestview::kGuestViewManagerKeyName)); | |
| 67 } | |
| 68 | |
| 69 content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely( | |
| 70 int guest_instance_id, | |
| 71 int embedder_render_process_id) { | |
| 72 if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id, | |
| 73 guest_instance_id)) { | |
| 74 return nullptr; | |
| 75 } | |
| 76 return GetGuestByInstanceID(guest_instance_id); | |
| 77 } | |
| 78 | |
| 79 void GuestViewManager::AttachGuest(int embedder_process_id, | |
| 80 int element_instance_id, | |
| 81 int guest_instance_id, | |
| 82 const base::DictionaryValue& attach_params) { | |
| 83 auto guest_view = GuestViewBase::From(embedder_process_id, guest_instance_id); | |
| 84 if (!guest_view) | |
| 85 return; | |
| 86 | |
| 87 ElementInstanceKey key(embedder_process_id, element_instance_id); | |
| 88 auto it = instance_id_map_.find(key); | |
| 89 // If there is an existing guest attached to the element, then destroy the | |
| 90 // existing guest. | |
| 91 if (it != instance_id_map_.end()) { | |
| 92 int old_guest_instance_id = it->second; | |
| 93 if (old_guest_instance_id == guest_instance_id) | |
| 94 return; | |
| 95 | |
| 96 auto old_guest_view = GuestViewBase::From(embedder_process_id, | |
| 97 old_guest_instance_id); | |
| 98 old_guest_view->Destroy(); | |
| 99 } | |
| 100 instance_id_map_[key] = guest_instance_id; | |
| 101 reverse_instance_id_map_[guest_instance_id] = key; | |
| 102 guest_view->SetAttachParams(attach_params); | |
| 103 } | |
| 104 | |
| 105 void GuestViewManager::DetachGuest(GuestViewBase* guest) { | |
| 106 if (!guest->attached()) | |
| 107 return; | |
| 108 | |
| 109 auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id()); | |
| 110 if (reverse_it == reverse_instance_id_map_.end()) | |
| 111 return; | |
| 112 | |
| 113 const ElementInstanceKey& key = reverse_it->second; | |
| 114 | |
| 115 auto it = instance_id_map_.find(key); | |
| 116 DCHECK(it != instance_id_map_.end()); | |
| 117 | |
| 118 reverse_instance_id_map_.erase(reverse_it); | |
| 119 instance_id_map_.erase(it); | |
| 120 } | |
| 121 | |
| 122 bool GuestViewManager::IsOwnedByExtension(GuestViewBase* guest) { | |
| 123 return delegate_->IsOwnedByExtension(guest); | |
| 124 } | |
| 125 | |
| 126 int GuestViewManager::GetNextInstanceID() { | |
| 127 return ++current_instance_id_; | |
| 128 } | |
| 129 | |
| 130 void GuestViewManager::CreateGuest(const std::string& view_type, | |
| 131 content::WebContents* owner_web_contents, | |
| 132 const base::DictionaryValue& create_params, | |
| 133 const WebContentsCreatedCallback& callback) { | |
| 134 GuestViewBase* guest = CreateGuestInternal(owner_web_contents, view_type); | |
| 135 if (!guest) { | |
| 136 callback.Run(nullptr); | |
| 137 return; | |
| 138 } | |
| 139 guest->Init(create_params, callback); | |
| 140 } | |
| 141 | |
| 142 content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams( | |
| 143 const std::string& view_type, | |
| 144 content::WebContents* owner_web_contents, | |
| 145 const content::WebContents::CreateParams& create_params) { | |
| 146 auto guest = CreateGuestInternal(owner_web_contents, view_type); | |
| 147 if (!guest) | |
| 148 return nullptr; | |
| 149 content::WebContents::CreateParams guest_create_params(create_params); | |
| 150 guest_create_params.guest_delegate = guest; | |
| 151 auto guest_web_contents = WebContents::Create(guest_create_params); | |
| 152 guest->InitWithWebContents(base::DictionaryValue(), guest_web_contents); | |
| 153 return guest_web_contents; | |
| 154 } | |
| 155 | |
| 156 content::WebContents* GuestViewManager::GetGuestByInstanceID( | |
| 157 int owner_process_id, | |
| 158 int element_instance_id) { | |
| 159 int guest_instance_id = GetGuestInstanceIDForElementID(owner_process_id, | |
| 160 element_instance_id); | |
| 161 if (guest_instance_id == guestview::kInstanceIDNone) | |
| 162 return nullptr; | |
| 163 | |
| 164 return GetGuestByInstanceID(guest_instance_id); | |
| 165 } | |
| 166 | |
| 167 int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id, | |
| 168 int element_instance_id) { | |
| 169 auto iter = instance_id_map_.find( | |
| 170 ElementInstanceKey(owner_process_id, element_instance_id)); | |
| 171 if (iter == instance_id_map_.end()) | |
| 172 return guestview::kInstanceIDNone; | |
| 173 return iter->second; | |
| 174 } | |
| 175 | |
| 176 SiteInstance* GuestViewManager::GetGuestSiteInstance( | |
| 177 const GURL& guest_site) { | |
| 178 for (const auto& guest : guest_web_contents_by_instance_id_) { | |
| 179 if (guest.second->GetSiteInstance()->GetSiteURL() == guest_site) | |
| 180 return guest.second->GetSiteInstance(); | |
| 181 } | |
| 182 return nullptr; | |
| 183 } | |
| 184 | |
| 185 bool GuestViewManager::ForEachGuest(WebContents* owner_web_contents, | |
| 186 const GuestCallback& callback) { | |
| 187 for (const auto& guest : guest_web_contents_by_instance_id_) { | |
| 188 auto guest_view = GuestViewBase::FromWebContents(guest.second); | |
| 189 if (guest_view->owner_web_contents() != owner_web_contents) | |
| 190 continue; | |
| 191 | |
| 192 if (callback.Run(guest_view->web_contents())) | |
| 193 return true; | |
| 194 } | |
| 195 return false; | |
| 196 } | |
| 197 | |
| 198 WebContents* GuestViewManager::GetFullPageGuest( | |
| 199 WebContents* embedder_web_contents) { | |
| 200 WebContents* result = nullptr; | |
| 201 ForEachGuest(embedder_web_contents, | |
| 202 base::Bind(&GuestViewManager::GetFullPageGuestHelper, &result)); | |
| 203 return result; | |
| 204 } | |
| 205 | |
| 206 void GuestViewManager::AddGuest(int guest_instance_id, | |
| 207 WebContents* guest_web_contents) { | |
| 208 CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id)); | |
| 209 CHECK(CanUseGuestInstanceID(guest_instance_id)); | |
| 210 guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents; | |
| 211 } | |
| 212 | |
| 213 void GuestViewManager::RemoveGuest(int guest_instance_id) { | |
| 214 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
| 215 DCHECK(it != guest_web_contents_by_instance_id_.end()); | |
| 216 guest_web_contents_by_instance_id_.erase(it); | |
| 217 | |
| 218 auto id_iter = reverse_instance_id_map_.find(guest_instance_id); | |
| 219 if (id_iter != reverse_instance_id_map_.end()) { | |
| 220 const ElementInstanceKey& instance_id_key = id_iter->second; | |
| 221 instance_id_map_.erase(instance_id_map_.find(instance_id_key)); | |
| 222 reverse_instance_id_map_.erase(id_iter); | |
| 223 } | |
| 224 | |
| 225 // All the instance IDs that lie within [0, last_instance_id_removed_] | |
| 226 // are invalid. | |
| 227 // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set. | |
| 228 // The following code compacts the set by incrementing | |
| 229 // |last_instance_id_removed_|. | |
| 230 if (guest_instance_id == last_instance_id_removed_ + 1) { | |
| 231 ++last_instance_id_removed_; | |
| 232 // Compact. | |
| 233 auto iter = removed_instance_ids_.begin(); | |
| 234 while (iter != removed_instance_ids_.end()) { | |
| 235 int instance_id = *iter; | |
| 236 // The sparse invalid IDs must not lie within | |
| 237 // [0, last_instance_id_removed_] | |
| 238 DCHECK(instance_id > last_instance_id_removed_); | |
| 239 if (instance_id != last_instance_id_removed_ + 1) | |
| 240 break; | |
| 241 ++last_instance_id_removed_; | |
| 242 removed_instance_ids_.erase(iter++); | |
| 243 } | |
| 244 } else { | |
| 245 removed_instance_ids_.insert(guest_instance_id); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 GuestViewBase* GuestViewManager::CreateGuestInternal( | |
| 250 content::WebContents* owner_web_contents, | |
| 251 const std::string& view_type) { | |
| 252 if (guest_view_registry_.empty()) | |
| 253 RegisterGuestViewTypes(); | |
| 254 | |
| 255 auto it = guest_view_registry_.find(view_type); | |
| 256 if (it == guest_view_registry_.end()) { | |
| 257 NOTREACHED(); | |
| 258 return nullptr; | |
| 259 } | |
| 260 | |
| 261 return it->second.Run(owner_web_contents); | |
| 262 } | |
| 263 | |
| 264 void GuestViewManager::RegisterGuestViewTypes() { | |
| 265 delegate_->RegisterAdditionalGuestViewTypes(); | |
| 266 } | |
| 267 | |
| 268 bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase* guest) { | |
| 269 return delegate_->IsGuestAvailableToContext(guest); | |
| 270 } | |
| 271 | |
| 272 void GuestViewManager::DispatchEvent(const std::string& event_name, | |
| 273 scoped_ptr<base::DictionaryValue> args, | |
| 274 GuestViewBase* guest, | |
| 275 int instance_id) { | |
| 276 delegate_->DispatchEvent(event_name, args.Pass(), guest, instance_id); | |
| 277 } | |
| 278 | |
| 279 content::WebContents* GuestViewManager::GetGuestByInstanceID( | |
| 280 int guest_instance_id) { | |
| 281 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
| 282 if (it == guest_web_contents_by_instance_id_.end()) | |
| 283 return nullptr; | |
| 284 return it->second; | |
| 285 } | |
| 286 | |
| 287 bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill( | |
| 288 int embedder_render_process_id, | |
| 289 int guest_instance_id) { | |
| 290 if (!CanEmbedderAccessInstanceID(embedder_render_process_id, | |
| 291 guest_instance_id)) { | |
| 292 // The embedder process is trying to access a guest it does not own. | |
| 293 content::RecordAction( | |
| 294 base::UserMetricsAction("BadMessageTerminate_BPGM")); | |
| 295 content::RenderProcessHost::FromID(embedder_render_process_id) | |
| 296 ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE, false); | |
| 297 return false; | |
| 298 } | |
| 299 return true; | |
| 300 } | |
| 301 | |
| 302 bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) { | |
| 303 if (guest_instance_id <= last_instance_id_removed_) | |
| 304 return false; | |
| 305 return !ContainsKey(removed_instance_ids_, guest_instance_id); | |
| 306 } | |
| 307 | |
| 308 // static | |
| 309 bool GuestViewManager::GetFullPageGuestHelper( | |
| 310 content::WebContents** result, | |
| 311 content::WebContents* guest_web_contents) { | |
| 312 auto guest_view = GuestViewBase::FromWebContents(guest_web_contents); | |
| 313 if (guest_view && guest_view->is_full_page_plugin()) { | |
| 314 *result = guest_web_contents; | |
| 315 return true; | |
| 316 } | |
| 317 return false; | |
| 318 } | |
| 319 | |
| 320 bool GuestViewManager::CanEmbedderAccessInstanceID( | |
| 321 int embedder_render_process_id, | |
| 322 int guest_instance_id) { | |
| 323 // The embedder is trying to access a guest with a negative or zero | |
| 324 // instance ID. | |
| 325 if (guest_instance_id <= guestview::kInstanceIDNone) | |
| 326 return false; | |
| 327 | |
| 328 // The embedder is trying to access an instance ID that has not yet been | |
| 329 // allocated by GuestViewManager. This could cause instance ID | |
| 330 // collisions in the future, and potentially give one embedder access to a | |
| 331 // guest it does not own. | |
| 332 if (guest_instance_id > current_instance_id_) | |
| 333 return false; | |
| 334 | |
| 335 // We might get some late arriving messages at tear down. Let's let the | |
| 336 // embedder tear down in peace. | |
| 337 auto it = guest_web_contents_by_instance_id_.find(guest_instance_id); | |
| 338 if (it == guest_web_contents_by_instance_id_.end()) | |
| 339 return true; | |
| 340 | |
| 341 auto guest_view = GuestViewBase::FromWebContents(it->second); | |
| 342 if (!guest_view) | |
| 343 return false; | |
| 344 | |
| 345 return embedder_render_process_id == | |
| 346 guest_view->owner_web_contents()->GetRenderProcessHost()->GetID(); | |
| 347 } | |
| 348 | |
| 349 GuestViewManager::ElementInstanceKey::ElementInstanceKey() | |
| 350 : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID), | |
| 351 element_instance_id(content::ChildProcessHost::kInvalidUniqueID) { | |
| 352 } | |
| 353 | |
| 354 GuestViewManager::ElementInstanceKey::ElementInstanceKey( | |
| 355 int embedder_process_id, | |
| 356 int element_instance_id) | |
| 357 : embedder_process_id(embedder_process_id), | |
| 358 element_instance_id(element_instance_id) { | |
| 359 } | |
| 360 | |
| 361 bool GuestViewManager::ElementInstanceKey::operator<( | |
| 362 const GuestViewManager::ElementInstanceKey& other) const { | |
| 363 if (embedder_process_id != other.embedder_process_id) | |
| 364 return embedder_process_id < other.embedder_process_id; | |
| 365 | |
| 366 return element_instance_id < other.element_instance_id; | |
| 367 } | |
| 368 | |
| 369 bool GuestViewManager::ElementInstanceKey::operator==( | |
| 370 const GuestViewManager::ElementInstanceKey& other) const { | |
| 371 return (embedder_process_id == other.embedder_process_id) && | |
| 372 (element_instance_id == other.element_instance_id); | |
| 373 } | |
| 374 | |
| 375 } // namespace extensions | |
| OLD | NEW |