1 // Copyright 2013 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 "chrome/browser/guestview/webview/webview_guest.h" 6 7 #include "base/command_line.h" 8 #include "base/strings/stringprintf.h" 9 #include "chrome/browser/extensions/api/web_request/web_request_api.h" 10 #include "chrome/browser/extensions/extension_renderer_state.h" 11 #include "chrome/browser/extensions/extension_web_contents_observer.h" 12 #include "chrome/browser/extensions/script_executor.h" 13 #include "chrome/browser/favicon/favicon_tab_helper.h" 14 #include "chrome/browser/guestview/guestview_constants.h" 15 #include "chrome/browser/guestview/webview/webview_constants.h" 16 #include "chrome/browser/guestview/webview/webview_permission_types.h" 17 #include "chrome/common/chrome_version_info.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/native_web_keyboard_event.h" 20 #include "content/public/browser/navigation_entry.h" 21 #include "content/public/browser/notification_details.h" 22 #include "content/public/browser/notification_source.h" 23 #include "content/public/browser/notification_types.h" 24 #include "content/public/browser/render_process_host.h" 25 #include "content/public/browser/resource_request_details.h" 26 #include "content/public/browser/site_instance.h" 27 #include "content/public/browser/storage_partition.h" 28 #include "content/public/browser/user_metrics.h" 29 #include "content/public/browser/web_contents.h" 30 #include "content/public/common/content_switches.h" 31 #include "content/public/common/result_codes.h" 32 #include "extensions/common/constants.h" 33 #include "net/base/net_errors.h" 34 35 #if defined(ENABLE_PLUGINS) 36 #include "chrome/browser/guestview/webview/plugin_permission_helper.h" 37 #endif 38 39 using content::WebContents; 40 using content::UserMetricsAction; 41 42 namespace { 43 44 static std::string TerminationStatusToString(base::TerminationStatus status) { 45 switch (status) { 46 case base::TERMINATION_STATUS_NORMAL_TERMINATION: 47 return "normal"; 48 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: 49 case base::TERMINATION_STATUS_STILL_RUNNING: 50 return "abnormal"; 51 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: 52 return "killed"; 53 case base::TERMINATION_STATUS_PROCESS_CRASHED: 54 #if defined(OS_ANDROID) 55 case base::TERMINATION_STATUS_OOM_PROTECTED: 56 #endif 57 return "crashed"; 58 case base::TERMINATION_STATUS_MAX_ENUM: 59 break; 60 } 61 NOTREACHED() << "Unknown Termination Status."; 62 return "unknown"; 63 } 64 65 static std::string PermissionTypeToString(BrowserPluginPermissionType type) { 66 switch (type) { 67 case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD: 68 return webview::kPermissionTypeDownload; 69 case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION: 70 return webview::kPermissionTypeGeolocation; 71 case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA: 72 return webview::kPermissionTypeMedia; 73 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW: 74 return webview::kPermissionTypeNewWindow; 75 case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK: 76 return webview::kPermissionTypePointerLock; 77 case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 78 return webview::kPermissionTypeDialog; 79 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN: 80 NOTREACHED(); 81 break; 82 default: { 83 WebViewPermissionType webview = static_cast<WebViewPermissionType>(type); 84 switch (webview) { 85 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 86 return webview::kPermissionTypeLoadPlugin; 87 } 88 NOTREACHED(); 89 } 90 } 91 return std::string(); 92 } 93 94 void RemoveWebViewEventListenersOnIOThread( 95 void* profile, 96 const std::string& extension_id, 97 int embedder_process_id, 98 int view_instance_id) { 99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 100 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners( 101 profile, 102 extension_id, 103 embedder_process_id, 104 view_instance_id); 105 } 106 107 void AttachWebViewHelpers(WebContents* contents) { 108 FaviconTabHelper::CreateForWebContents(contents); 109 extensions::ExtensionWebContentsObserver::CreateForWebContents(contents); 110 #if defined(ENABLE_PLUGINS) 111 PluginPermissionHelper::CreateForWebContents(contents); 112 #endif 113 } 114 115 } // namespace 116 117 WebViewGuest::WebViewGuest(WebContents* guest_web_contents, 118 const std::string& extension_id) 119 : GuestView(guest_web_contents, extension_id), 120 WebContentsObserver(guest_web_contents), 121 script_executor_(new extensions::ScriptExecutor(guest_web_contents, 122 &script_observers_)), 123 next_permission_request_id_(0), 124 is_overriding_user_agent_(false), 125 pending_reload_on_attachment_(false) { 126 notification_registrar_.Add( 127 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, 128 content::Source<WebContents>(guest_web_contents)); 129 130 notification_registrar_.Add( 131 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 132 content::Source<WebContents>(guest_web_contents)); 133 134 AttachWebViewHelpers(guest_web_contents); 135 } 136 137 // static 138 WebViewGuest* WebViewGuest::From(int embedder_process_id, 139 int guest_instance_id) { 140 GuestView* guest = GuestView::From(embedder_process_id, guest_instance_id); 141 if (!guest) 142 return NULL; 143 return guest->AsWebView(); 144 } 145 146 // static 147 WebViewGuest* WebViewGuest::FromWebContents(WebContents* contents) { 148 GuestView* guest = GuestView::FromWebContents(contents); 149 return guest ? guest->AsWebView() : NULL; 150 } 151 152 // static 153 void WebViewGuest::RecordUserInitiatedUMA(const PermissionResponseInfo& info, 154 bool allow) { 155 if (allow) { 156 // Note that |allow| == true means the embedder explicitly allowed the 157 // request. For some requests they might still fail. An example of such 158 // scenario would be: an embedder allows geolocation request but doesn't 159 // have geolocation access on its own. 160 switch (info.permission_type) { 161 case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD: 162 RecordAction( 163 UserMetricsAction("BrowserPlugin.PermissionAllow.Download")); 164 break; 165 case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION: 166 RecordAction( 167 UserMetricsAction("BrowserPlugin.PermissionAllow.Geolocation")); 168 break; 169 case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA: 170 RecordAction( 171 UserMetricsAction("BrowserPlugin.PermissionAllow.Media")); 172 break; 173 case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK: 174 RecordAction( 175 UserMetricsAction("BrowserPlugin.PermissionAllow.PointerLock")); 176 break; 177 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW: 178 RecordAction( 179 UserMetricsAction("BrowserPlugin.PermissionAllow.NewWindow")); 180 break; 181 case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 182 RecordAction( 183 UserMetricsAction("BrowserPlugin.PermissionAllow.JSDialog")); 184 break; 185 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN: 186 break; 187 default: { 188 WebViewPermissionType webview_permission_type = 189 static_cast<WebViewPermissionType>(info.permission_type); 190 switch (webview_permission_type) { 191 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 192 RecordAction( 193 UserMetricsAction("WebView.Guest.PermissionAllow.PluginLoad")); 194 break; 195 default: 196 break; 197 } 198 } 199 } 200 } else { 201 switch (info.permission_type) { 202 case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD: 203 RecordAction( 204 UserMetricsAction("BrowserPlugin.PermissionDeny.Download")); 205 break; 206 case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION: 207 RecordAction( 208 UserMetricsAction("BrowserPlugin.PermissionDeny.Geolocation")); 209 break; 210 case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA: 211 RecordAction( 212 UserMetricsAction("BrowserPlugin.PermissionDeny.Media")); 213 break; 214 case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK: 215 RecordAction( 216 UserMetricsAction("BrowserPlugin.PermissionDeny.PointerLock")); 217 break; 218 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW: 219 RecordAction( 220 UserMetricsAction("BrowserPlugin.PermissionDeny.NewWindow")); 221 break; 222 case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG: 223 RecordAction( 224 UserMetricsAction("BrowserPlugin.PermissionDeny.JSDialog")); 225 break; 226 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN: 227 break; 228 default: { 229 WebViewPermissionType webview_permission_type = 230 static_cast<WebViewPermissionType>(info.permission_type); 231 switch (webview_permission_type) { 232 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN: 233 RecordAction( 234 UserMetricsAction("WebView.Guest.PermissionDeny.PluginLoad")); 235 break; 236 default: 237 break; 238 } 239 } 240 } 241 } 242 } 243 244 void WebViewGuest::Attach(WebContents* embedder_web_contents, 245 const base::DictionaryValue& args) { 246 std::string user_agent_override; 247 if (args.GetString(webview::kParameterUserAgentOverride, 248 &user_agent_override)) { 249 SetUserAgentOverride(user_agent_override); 250 } else { 251 SetUserAgentOverride(""); 252 } 253 254 GuestView::Attach(embedder_web_contents, args); 255 256 AddWebViewToExtensionRendererState(); 257 } 258 259 GuestView::Type WebViewGuest::GetViewType() const { 260 return GuestView::WEBVIEW; 261 } 262 263 WebViewGuest* WebViewGuest::AsWebView() { 264 return this; 265 } 266 267 AdViewGuest* WebViewGuest::AsAdView() { 268 return NULL; 269 } 270 271 void WebViewGuest::AddMessageToConsole(int32 level, 272 const base::string16& message, 273 int32 line_no, 274 const base::string16& source_id) { 275 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 276 // Log levels are from base/logging.h: LogSeverity. 277 args->SetInteger(webview::kLevel, level); 278 args->SetString(webview::kMessage, message); 279 args->SetInteger(webview::kLine, line_no); 280 args->SetString(webview::kSourceId, source_id); 281 DispatchEvent( 282 new GuestView::Event(webview::kEventConsoleMessage, args.Pass())); 283 } 284 285 void WebViewGuest::Close() { 286 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 287 DispatchEvent(new GuestView::Event(webview::kEventClose, args.Pass())); 288 } 289 290 void WebViewGuest::DidAttach() { 291 if (pending_reload_on_attachment_) { 292 pending_reload_on_attachment_ = false; 293 guest_web_contents()->GetController().Reload(false); 294 } 295 } 296 297 void WebViewGuest::EmbedderDestroyed() { 298 // TODO(fsamuel): WebRequest event listeners for <webview> should survive 299 // reparenting of a <webview> within a single embedder. Right now, we keep 300 // around the browser state for the listener for the lifetime of the embedder. 301 // Ideally, the lifetime of the listeners should match the lifetime of the 302 // <webview> DOM node. Once http://crbug.com/156219 is resolved we can move 303 // the call to RemoveWebViewEventListenersOnIOThread back to 304 // WebViewGuest::WebContentsDestroyed. 305 content::BrowserThread::PostTask( 306 content::BrowserThread::IO, 307 FROM_HERE, 308 base::Bind( 309 &RemoveWebViewEventListenersOnIOThread, 310 browser_context(), extension_id(), 311 embedder_render_process_id(), 312 view_instance_id())); 313 } 314 315 void WebViewGuest::GuestProcessGone(base::TerminationStatus status) { 316 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 317 args->SetInteger(webview::kProcessId, 318 web_contents()->GetRenderProcessHost()->GetID()); 319 args->SetString(webview::kReason, TerminationStatusToString(status)); 320 DispatchEvent( 321 new GuestView::Event(webview::kEventExit, args.Pass())); 322 } 323 324 bool WebViewGuest::HandleKeyboardEvent( 325 const content::NativeWebKeyboardEvent& event) { 326 if (event.type != blink::WebInputEvent::RawKeyDown) 327 return false; 328 329 #if defined(OS_MACOSX) 330 if (event.modifiers != blink::WebInputEvent::MetaKey) 331 return false; 332 333 if (event.windowsKeyCode == ui::VKEY_OEM_4) { 334 Go(-1); 335 return true; 336 } 337 338 if (event.windowsKeyCode == ui::VKEY_OEM_6) { 339 Go(1); 340 return true; 341 } 342 #else 343 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) { 344 Go(-1); 345 return true; 346 } 347 348 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) { 349 Go(1); 350 return true; 351 } 352 #endif 353 return false; 354 } 355 356 bool WebViewGuest::IsDragAndDropEnabled() { 357 #if defined(OS_CHROMEOS) 358 return true; 359 #else 360 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 361 if (channel != chrome::VersionInfo::CHANNEL_STABLE && 362 channel != chrome::VersionInfo::CHANNEL_BETA) { 363 // Drag and drop is enabled in canary and dev channel. 364 return true; 365 } 366 367 return CommandLine::ForCurrentProcess()->HasSwitch( 368 switches::kEnableBrowserPluginDragDrop); 369 #endif 370 } 371 372 bool WebViewGuest::IsOverridingUserAgent() const { 373 return is_overriding_user_agent_; 374 } 375 376 void WebViewGuest::LoadProgressed(double progress) { 377 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 378 args->SetString(guestview::kUrl, web_contents()->GetURL().spec()); 379 args->SetDouble(webview::kProgress, progress); 380 DispatchEvent(new GuestView::Event(webview::kEventLoadProgress, args.Pass())); 381 } 382 383 void WebViewGuest::LoadAbort(bool is_top_level, 384 const GURL& url, 385 const std::string& error_type) { 386 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 387 args->SetBoolean(guestview::kIsTopLevel, is_top_level); 388 args->SetString(guestview::kUrl, url.possibly_invalid_spec()); 389 args->SetString(guestview::kReason, error_type); 390 DispatchEvent(new GuestView::Event(webview::kEventLoadAbort, args.Pass())); 391 } 392 393 // TODO(fsamuel): Find a reliable way to test the 'responsive' and 394 // 'unresponsive' events. 395 void WebViewGuest::RendererResponsive() { 396 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 397 args->SetInteger(webview::kProcessId, 398 guest_web_contents()->GetRenderProcessHost()->GetID()); 399 DispatchEvent(new GuestView::Event(webview::kEventResponsive, args.Pass())); 400 } 401 402 void WebViewGuest::RendererUnresponsive() { 403 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 404 args->SetInteger(webview::kProcessId, 405 guest_web_contents()->GetRenderProcessHost()->GetID()); 406 DispatchEvent(new GuestView::Event(webview::kEventUnresponsive, args.Pass())); 407 } 408 409 bool WebViewGuest::RequestPermission( 410 BrowserPluginPermissionType permission_type, 411 const base::DictionaryValue& request_info, 412 const PermissionResponseCallback& callback, 413 bool allowed_by_default) { 414 // If there are too many pending permission requests then reject this request. 415 if (pending_permission_requests_.size() >= 416 webview::kMaxOutstandingPermissionRequests) { 417 callback.Run(false, std::string()); 418 return true; 419 } 420 421 int request_id = next_permission_request_id_++; 422 pending_permission_requests_[request_id] = 423 PermissionResponseInfo(callback, permission_type, allowed_by_default); 424 scoped_ptr<base::DictionaryValue> args(request_info.DeepCopy()); 425 args->SetInteger(webview::kRequestId, request_id); 426 switch (permission_type) { 427 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW: { 428 DispatchEvent(new GuestView::Event(webview::kEventNewWindow, 429 args.Pass())); 430 break; 431 } 432 case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG: { 433 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 434 if (channel > chrome::VersionInfo::CHANNEL_DEV) { 435 // 'dialog' API is not available in stable/beta. 436 callback.Run(false, std::string()); 437 return true; 438 } 439 DispatchEvent(new GuestView::Event(webview::kEventDialog, 440 args.Pass())); 441 break; 442 } 443 default: { 444 args->SetString(webview::kPermission, 445 PermissionTypeToString(permission_type)); 446 DispatchEvent(new GuestView::Event(webview::kEventPermissionRequest, 447 args.Pass())); 448 break; 449 } 450 } 451 return true; 452 } 453 454 void WebViewGuest::Observe(int type, 455 const content::NotificationSource& source, 456 const content::NotificationDetails& details) { 457 switch (type) { 458 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: { 459 DCHECK_EQ(content::Source<WebContents>(source).ptr(), 460 guest_web_contents()); 461 if (content::Source<WebContents>(source).ptr() == guest_web_contents()) 462 LoadHandlerCalled(); 463 break; 464 } 465 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { 466 DCHECK_EQ(content::Source<WebContents>(source).ptr(), 467 guest_web_contents()); 468 content::ResourceRedirectDetails* resource_redirect_details = 469 content::Details<content::ResourceRedirectDetails>(details).ptr(); 470 bool is_top_level = 471 resource_redirect_details->resource_type == ResourceType::MAIN_FRAME; 472 LoadRedirect(resource_redirect_details->url, 473 resource_redirect_details->new_url, 474 is_top_level); 475 break; 476 } 477 default: 478 NOTREACHED() << "Unexpected notification sent."; 479 break; 480 } 481 } 482 483 void WebViewGuest::Go(int relative_index) { 484 guest_web_contents()->GetController().GoToOffset(relative_index); 485 } 486 487 void WebViewGuest::Reload() { 488 // TODO(fsamuel): Don't check for repost because we don't want to show 489 // Chromium's repost warning. We might want to implement a separate API 490 // for registering a callback if a repost is about to happen. 491 guest_web_contents()->GetController().Reload(false); 492 } 493 494 WebViewGuest::SetPermissionResult WebViewGuest::SetPermission( 495 int request_id, 496 PermissionResponseAction action, 497 const std::string& user_input) { 498 RequestMap::iterator request_itr = 499 pending_permission_requests_.find(request_id); 500 501 if (request_itr == pending_permission_requests_.end()) 502 return SET_PERMISSION_INVALID; 503 504 const PermissionResponseInfo& info = request_itr->second; 505 bool allow = (action == ALLOW) || 506 ((action == DEFAULT) && info.allowed_by_default); 507 508 info.callback.Run(allow, user_input); 509 510 // Only record user initiated (i.e. non-default) actions. 511 if (action != DEFAULT) 512 RecordUserInitiatedUMA(info, allow); 513 514 pending_permission_requests_.erase(request_itr); 515 516 return allow ? SET_PERMISSION_ALLOWED : SET_PERMISSION_DENIED; 517 } 518 519 void WebViewGuest::SetUserAgentOverride( 520 const std::string& user_agent_override) { 521 is_overriding_user_agent_ = !user_agent_override.empty(); 522 if (is_overriding_user_agent_) { 523 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA")); 524 } 525 guest_web_contents()->SetUserAgentOverride(user_agent_override); 526 } 527 528 void WebViewGuest::Stop() { 529 guest_web_contents()->Stop(); 530 } 531 532 void WebViewGuest::Terminate() { 533 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate")); 534 base::ProcessHandle process_handle = 535 guest_web_contents()->GetRenderProcessHost()->GetHandle(); 536 if (process_handle) 537 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false); 538 } 539 540 bool WebViewGuest::ClearData(const base::Time remove_since, 541 uint32 removal_mask, 542 const base::Closure& callback) { 543 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData")); 544 content::StoragePartition* partition = 545 content::BrowserContext::GetStoragePartition( 546 web_contents()->GetBrowserContext(), 547 web_contents()->GetSiteInstance()); 548 549 if (!partition) 550 return false; 551 552 partition->ClearData( 553 removal_mask, 554 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, 555 NULL, 556 content::StoragePartition::OriginMatcherFunction(), 557 remove_since, 558 base::Time::Now(), 559 callback); 560 return true; 561 } 562 563 WebViewGuest::~WebViewGuest() { 564 } 565 566 void WebViewGuest::DidCommitProvisionalLoadForFrame( 567 int64 frame_id, 568 const base::string16& frame_unique_name, 569 bool is_main_frame, 570 const GURL& url, 571 content::PageTransition transition_type, 572 content::RenderViewHost* render_view_host) { 573 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 574 args->SetString(guestview::kUrl, url.spec()); 575 args->SetBoolean(guestview::kIsTopLevel, is_main_frame); 576 args->SetInteger(webview::kInternalCurrentEntryIndex, 577 web_contents()->GetController().GetCurrentEntryIndex()); 578 args->SetInteger(webview::kInternalEntryCount, 579 web_contents()->GetController().GetEntryCount()); 580 args->SetInteger(webview::kInternalProcessId, 581 web_contents()->GetRenderProcessHost()->GetID()); 582 DispatchEvent(new GuestView::Event(webview::kEventLoadCommit, args.Pass())); 583 } 584 585 void WebViewGuest::DidFailProvisionalLoad( 586 int64 frame_id, 587 const base::string16& frame_unique_name, 588 bool is_main_frame, 589 const GURL& validated_url, 590 int error_code, 591 const base::string16& error_description, 592 content::RenderViewHost* render_view_host) { 593 // Translate the |error_code| into an error string. 594 std::string error_type; 595 base::RemoveChars(net::ErrorToString(error_code), "net::", &error_type); 596 LoadAbort(is_main_frame, validated_url, error_type); 597 } 598 599 void WebViewGuest::DidStartProvisionalLoadForFrame( 600 int64 frame_id, 601 int64 parent_frame_id, 602 bool is_main_frame, 603 const GURL& validated_url, 604 bool is_error_page, 605 bool is_iframe_srcdoc, 606 content::RenderViewHost* render_view_host) { 607 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 608 args->SetString(guestview::kUrl, validated_url.spec()); 609 args->SetBoolean(guestview::kIsTopLevel, is_main_frame); 610 DispatchEvent(new GuestView::Event(webview::kEventLoadStart, args.Pass())); 611 } 612 613 void WebViewGuest::DidStopLoading(content::RenderViewHost* render_view_host) { 614 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 615 DispatchEvent(new GuestView::Event(webview::kEventLoadStop, args.Pass())); 616 } 617 618 void WebViewGuest::WebContentsDestroyed(WebContents* web_contents) { 619 RemoveWebViewFromExtensionRendererState(web_contents); 620 } 621 622 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) { 623 content::NavigationController& controller = 624 guest_web_contents()->GetController(); 625 content::NavigationEntry* entry = controller.GetVisibleEntry(); 626 if (!entry) 627 return; 628 entry->SetIsOverridingUserAgent(!user_agent.empty()); 629 if (!attached()) { 630 // We cannot reload now because all resource loads are suspended until 631 // attachment. 632 pending_reload_on_attachment_ = true; 633 return; 634 } 635 guest_web_contents()->GetController().Reload(false); 636 } 637 638 void WebViewGuest::LoadHandlerCalled() { 639 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 640 DispatchEvent(new GuestView::Event(webview::kEventContentLoad, args.Pass())); 641 } 642 643 void WebViewGuest::LoadRedirect(const GURL& old_url, 644 const GURL& new_url, 645 bool is_top_level) { 646 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 647 args->SetBoolean(guestview::kIsTopLevel, is_top_level); 648 args->SetString(webview::kNewURL, new_url.spec()); 649 args->SetString(webview::kOldURL, old_url.spec()); 650 DispatchEvent(new GuestView::Event(webview::kEventLoadRedirect, args.Pass())); 651 } 652 653 // static 654 bool WebViewGuest::AllowChromeExtensionURLs() { 655 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 656 return channel <= chrome::VersionInfo::CHANNEL_DEV; 657 } 658 659 void WebViewGuest::AddWebViewToExtensionRendererState() { 660 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL(); 661 std::string partition_domain; 662 std::string partition_id; 663 bool in_memory; 664 if (!GetGuestPartitionConfigForSite( 665 site_url, &partition_domain, &partition_id, &in_memory)) { 666 NOTREACHED(); 667 return; 668 } 669 DCHECK(extension_id() == partition_domain); 670 671 ExtensionRendererState::WebViewInfo webview_info; 672 webview_info.embedder_process_id = embedder_render_process_id(); 673 webview_info.instance_id = view_instance_id(); 674 webview_info.partition_id = partition_id; 675 webview_info.extension_id = extension_id(); 676 webview_info.allow_chrome_extension_urls = AllowChromeExtensionURLs(); 677 678 content::BrowserThread::PostTask( 679 content::BrowserThread::IO, FROM_HERE, 680 base::Bind( 681 &ExtensionRendererState::AddWebView, 682 base::Unretained(ExtensionRendererState::GetInstance()), 683 guest_web_contents()->GetRenderProcessHost()->GetID(), 684 guest_web_contents()->GetRoutingID(), 685 webview_info)); 686 } 687 688 // static 689 void WebViewGuest::RemoveWebViewFromExtensionRendererState( 690 WebContents* web_contents) { 691 content::BrowserThread::PostTask( 692 content::BrowserThread::IO, FROM_HERE, 693 base::Bind( 694 &ExtensionRendererState::RemoveWebView, 695 base::Unretained(ExtensionRendererState::GetInstance()), 696 web_contents->GetRenderProcessHost()->GetID(), 697 web_contents->GetRoutingID())); 698 } 699 700 GURL WebViewGuest::ResolveURL(const std::string& src) { 701 if (extension_id().empty()) { 702 NOTREACHED(); 703 return GURL(src); 704 } 705 706 // Only resolve URL to chrome-extension:// if we support such URLs. 707 if (!AllowChromeExtensionURLs()) 708 return GURL(src); 709 710 GURL default_url(base::StringPrintf("%s://%s/", 711 extensions::kExtensionScheme, 712 extension_id().c_str())); 713 return default_url.Resolve(src); 714 } 715 716 void WebViewGuest::SizeChanged(const gfx::Size& old_size, 717 const gfx::Size& new_size) { 718 scoped_ptr<DictionaryValue> args(new DictionaryValue()); 719 args->SetInteger(webview::kOldHeight, old_size.height()); 720 args->SetInteger(webview::kOldWidth, old_size.width()); 721 args->SetInteger(webview::kNewHeight, new_size.height()); 722 args->SetInteger(webview::kNewWidth, new_size.width()); 723 DispatchEvent(new GuestView::Event(webview::kEventSizeChanged, args.Pass())); 724 } 725 726 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo() 727 : permission_type(BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN), 728 allowed_by_default(false) { 729 } 730 731 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo( 732 const PermissionResponseCallback& callback, 733 BrowserPluginPermissionType permission_type, 734 bool allowed_by_default) 735 : callback(callback), 736 permission_type(permission_type), 737 allowed_by_default(allowed_by_default) { 738 } 739 740 WebViewGuest::PermissionResponseInfo::~PermissionResponseInfo() { 741 } 742