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.
5 #include "chrome/browser/guest_view/web_view/web_view_guest.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
11 #include "chrome/browser/extensions/api/webview/webview_api.h"
12 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
13 #include "chrome/browser/extensions/extension_renderer_state.h"
14 #include "chrome/browser/extensions/menu_manager.h"
15 #include "chrome/browser/extensions/script_executor.h"
16 #include "chrome/browser/favicon/favicon_tab_helper.h"
17 #include "chrome/browser/guest_view/guest_view_constants.h"
18 #include "chrome/browser/guest_view/web_view/web_view_constants.h"
19 #include "chrome/browser/guest_view/web_view/web_view_permission_types.h"
20 #include "chrome/browser/renderer_context_menu/context_menu_delegate.h"
21 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
22 #include "chrome/common/chrome_version_info.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/geolocation_permission_context.h"
25 #include "content/public/browser/native_web_keyboard_event.h"
26 #include "content/public/browser/navigation_entry.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/resource_request_details.h"
33 #include "content/public/browser/site_instance.h"
34 #include "content/public/browser/storage_partition.h"
35 #include "content/public/browser/user_metrics.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_delegate.h"
38 #include "content/public/common/media_stream_request.h"
39 #include "content/public/common/page_zoom.h"
40 #include "content/public/common/result_codes.h"
41 #include "content/public/common/stop_find_action.h"
42 #include "extensions/common/constants.h"
43 #include "net/base/net_errors.h"
44 #include "third_party/WebKit/public/web/WebFindOptions.h"
45 #include "ui/base/models/simple_menu_model.h"
47 #if defined(ENABLE_PLUGINS)
48 #include "chrome/browser/guest_view/web_view/plugin_permission_helper.h"
51 #if defined(OS_CHROMEOS)
52 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
55 using base::UserMetricsAction
;
56 using content::WebContents
;
60 static std::string
TerminationStatusToString(base::TerminationStatus status
) {
62 case base::TERMINATION_STATUS_NORMAL_TERMINATION
:
64 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
65 case base::TERMINATION_STATUS_STILL_RUNNING
:
67 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
69 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
70 #if defined(OS_ANDROID)
71 case base::TERMINATION_STATUS_OOM_PROTECTED
:
74 case base::TERMINATION_STATUS_MAX_ENUM
:
77 NOTREACHED() << "Unknown Termination Status.";
81 static std::string
PermissionTypeToString(BrowserPluginPermissionType type
) {
83 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW
:
84 return webview::kPermissionTypeNewWindow
;
85 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN
:
89 WebViewPermissionType webview
= static_cast<WebViewPermissionType
>(type
);
91 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
92 return webview::kPermissionTypeDownload
;
93 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
94 return webview::kPermissionTypeGeolocation
;
95 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
96 return webview::kPermissionTypeDialog
;
97 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
98 return webview::kPermissionTypeLoadPlugin
;
99 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
100 return webview::kPermissionTypeMedia
;
101 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
102 return webview::kPermissionTypePointerLock
;
107 return std::string();
110 void RemoveWebViewEventListenersOnIOThread(
112 const std::string
& extension_id
,
113 int embedder_process_id
,
114 int view_instance_id
) {
115 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
116 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
123 void AttachWebViewHelpers(WebContents
* contents
) {
124 FaviconTabHelper::CreateForWebContents(contents
);
125 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
127 #if defined(ENABLE_PLUGINS)
128 PluginPermissionHelper::CreateForWebContents(contents
);
134 WebViewGuest::WebViewGuest(WebContents
* guest_web_contents
,
135 const std::string
& extension_id
)
136 : GuestView
<WebViewGuest
>(guest_web_contents
, extension_id
),
137 WebContentsObserver(guest_web_contents
),
138 script_executor_(new extensions::ScriptExecutor(guest_web_contents
,
139 &script_observers_
)),
140 current_context_menu_request_id_(0),
141 next_permission_request_id_(0),
142 is_overriding_user_agent_(false),
143 pending_reload_on_attachment_(false),
145 chromevox_injected_(false),
147 javascript_dialog_helper_(this) {
148 notification_registrar_
.Add(
149 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
150 content::Source
<WebContents
>(guest_web_contents
));
152 notification_registrar_
.Add(
153 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
154 content::Source
<WebContents
>(guest_web_contents
));
156 #if defined(OS_CHROMEOS)
157 chromeos::AccessibilityManager
* accessibility_manager
=
158 chromeos::AccessibilityManager::Get();
159 CHECK(accessibility_manager
);
160 accessibility_subscription_
= accessibility_manager
->RegisterCallback(
161 base::Bind(&WebViewGuest::OnAccessibilityStatusChanged
,
162 base::Unretained(this)));
165 AttachWebViewHelpers(guest_web_contents
);
169 const char WebViewGuest::Type
[] = "webview";
172 int WebViewGuest::GetViewInstanceId(WebContents
* contents
) {
173 WebViewGuest
* guest
= FromWebContents(contents
);
175 return guestview::kInstanceIDNone
;
177 return guest
->view_instance_id();
181 void WebViewGuest::RecordUserInitiatedUMA(const PermissionResponseInfo
& info
,
184 // Note that |allow| == true means the embedder explicitly allowed the
185 // request. For some requests they might still fail. An example of such
186 // scenario would be: an embedder allows geolocation request but doesn't
187 // have geolocation access on its own.
188 switch (info
.permission_type
) {
189 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW
:
190 content::RecordAction(
191 UserMetricsAction("BrowserPlugin.PermissionAllow.NewWindow"));
193 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN
:
196 WebViewPermissionType webview_permission_type
=
197 static_cast<WebViewPermissionType
>(info
.permission_type
);
198 switch (webview_permission_type
) {
199 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
200 content::RecordAction(
201 UserMetricsAction("WebView.PermissionAllow.Download"));
203 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
204 content::RecordAction(
205 UserMetricsAction("WebView.PermissionAllow.Geolocation"));
207 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
208 content::RecordAction(
209 UserMetricsAction("WebView.PermissionAllow.JSDialog"));
211 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
212 content::RecordAction(
213 UserMetricsAction("WebView.Guest.PermissionAllow.PluginLoad"));
214 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
215 content::RecordAction(
216 UserMetricsAction("WebView.PermissionAllow.Media"));
218 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
219 content::RecordAction(
220 UserMetricsAction("WebView.PermissionAllow.PointerLock"));
228 switch (info
.permission_type
) {
229 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW
:
230 content::RecordAction(
231 UserMetricsAction("BrowserPlugin.PermissionDeny.NewWindow"));
233 case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN
:
236 WebViewPermissionType webview_permission_type
=
237 static_cast<WebViewPermissionType
>(info
.permission_type
);
238 switch (webview_permission_type
) {
239 case WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
:
240 content::RecordAction(
241 UserMetricsAction("WebView.PermissionDeny.Download"));
243 case WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
:
244 content::RecordAction(
245 UserMetricsAction("WebView.PermissionDeny.Geolocation"));
247 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
:
248 content::RecordAction(
249 UserMetricsAction("WebView.PermissionDeny.JSDialog"));
251 case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN
:
252 content::RecordAction(
253 UserMetricsAction("WebView.Guest.PermissionDeny.PluginLoad"));
254 case WEB_VIEW_PERMISSION_TYPE_MEDIA
:
255 content::RecordAction(
256 UserMetricsAction("WebView.PermissionDeny.Media"));
258 case WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
:
259 content::RecordAction(
260 UserMetricsAction("WebView.PermissionDeny.PointerLock"));
271 scoped_ptr
<base::ListValue
> WebViewGuest::MenuModelToValue(
272 const ui::SimpleMenuModel
& menu_model
) {
273 scoped_ptr
<base::ListValue
> items(new base::ListValue());
274 for (int i
= 0; i
< menu_model
.GetItemCount(); ++i
) {
275 base::DictionaryValue
* item_value
= new base::DictionaryValue();
276 // TODO(lazyboy): We need to expose some kind of enum equivalent of
277 // |command_id| instead of plain integers.
278 item_value
->SetInteger(webview::kMenuItemCommandId
,
279 menu_model
.GetCommandIdAt(i
));
280 item_value
->SetString(webview::kMenuItemLabel
, menu_model
.GetLabelAt(i
));
281 items
->Append(item_value
);
286 void WebViewGuest::Attach(WebContents
* embedder_web_contents
,
287 const base::DictionaryValue
& args
) {
288 std::string user_agent_override
;
289 if (args
.GetString(webview::kParameterUserAgentOverride
,
290 &user_agent_override
)) {
291 SetUserAgentOverride(user_agent_override
);
293 SetUserAgentOverride("");
296 GuestViewBase::Attach(embedder_web_contents
, args
);
298 AddWebViewToExtensionRendererState();
301 bool WebViewGuest::HandleContextMenu(
302 const content::ContextMenuParams
& params
) {
303 ContextMenuDelegate
* menu_delegate
=
304 ContextMenuDelegate::FromWebContents(guest_web_contents());
305 DCHECK(menu_delegate
);
307 scoped_ptr
<RenderViewContextMenu
> menu
=
308 menu_delegate
->BuildMenu(guest_web_contents(), params
);
310 // Pass it to embedder.
311 int request_id
= ++current_context_menu_request_id_
;
312 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
313 scoped_ptr
<base::ListValue
> items
= MenuModelToValue(menu
->menu_model());
314 args
->Set(webview::kContextMenuItems
, items
.release());
315 args
->SetInteger(webview::kRequestId
, request_id
);
316 DispatchEvent(new GuestView::Event(webview::kEventContextMenu
, args
.Pass()));
318 menu_delegate
->ShowMenu(menu
.Pass());
322 void WebViewGuest::AddMessageToConsole(int32 level
,
323 const base::string16
& message
,
325 const base::string16
& source_id
) {
326 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
327 // Log levels are from base/logging.h: LogSeverity.
328 args
->SetInteger(webview::kLevel
, level
);
329 args
->SetString(webview::kMessage
, message
);
330 args
->SetInteger(webview::kLine
, line_no
);
331 args
->SetString(webview::kSourceId
, source_id
);
333 new GuestViewBase::Event(webview::kEventConsoleMessage
, args
.Pass()));
336 void WebViewGuest::Close() {
337 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
338 DispatchEvent(new GuestViewBase::Event(webview::kEventClose
, args
.Pass()));
341 void WebViewGuest::DidAttach() {
342 if (pending_reload_on_attachment_
) {
343 pending_reload_on_attachment_
= false;
344 guest_web_contents()->GetController().Reload(false);
348 void WebViewGuest::EmbedderDestroyed() {
349 // TODO(fsamuel): WebRequest event listeners for <webview> should survive
350 // reparenting of a <webview> within a single embedder. Right now, we keep
351 // around the browser state for the listener for the lifetime of the embedder.
352 // Ideally, the lifetime of the listeners should match the lifetime of the
353 // <webview> DOM node. Once http://crbug.com/156219 is resolved we can move
354 // the call to RemoveWebViewEventListenersOnIOThread back to
355 // WebViewGuest::WebContentsDestroyed.
356 content::BrowserThread::PostTask(
357 content::BrowserThread::IO
,
360 &RemoveWebViewEventListenersOnIOThread
,
361 browser_context(), embedder_extension_id(),
362 embedder_render_process_id(),
363 view_instance_id()));
366 void WebViewGuest::FindReply(int request_id
,
367 int number_of_matches
,
368 const gfx::Rect
& selection_rect
,
369 int active_match_ordinal
,
371 find_helper_
.FindReply(request_id
, number_of_matches
, selection_rect
,
372 active_match_ordinal
, final_update
);
375 void WebViewGuest::GuestProcessGone(base::TerminationStatus status
) {
376 // Cancel all find sessions in progress.
377 find_helper_
.CancelAllFindSessions();
379 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
380 args
->SetInteger(webview::kProcessId
,
381 guest_web_contents()->GetRenderProcessHost()->GetID());
382 args
->SetString(webview::kReason
, TerminationStatusToString(status
));
383 DispatchEvent(new GuestViewBase::Event(webview::kEventExit
, args
.Pass()));
386 bool WebViewGuest::HandleKeyboardEvent(
387 const content::NativeWebKeyboardEvent
& event
) {
388 if (event
.type
!= blink::WebInputEvent::RawKeyDown
)
391 #if defined(OS_MACOSX)
392 if (event
.modifiers
!= blink::WebInputEvent::MetaKey
)
395 if (event
.windowsKeyCode
== ui::VKEY_OEM_4
) {
400 if (event
.windowsKeyCode
== ui::VKEY_OEM_6
) {
405 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_BACK
) {
410 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_FORWARD
) {
418 bool WebViewGuest::IsDragAndDropEnabled() {
422 bool WebViewGuest::IsOverridingUserAgent() const {
423 return is_overriding_user_agent_
;
426 void WebViewGuest::LoadProgressed(double progress
) {
427 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
428 args
->SetString(guestview::kUrl
, guest_web_contents()->GetURL().spec());
429 args
->SetDouble(webview::kProgress
, progress
);
431 new GuestViewBase::Event(webview::kEventLoadProgress
, args
.Pass()));
434 void WebViewGuest::LoadAbort(bool is_top_level
,
436 const std::string
& error_type
) {
437 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
438 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
439 args
->SetString(guestview::kUrl
, url
.possibly_invalid_spec());
440 args
->SetString(guestview::kReason
, error_type
);
442 new GuestViewBase::Event(webview::kEventLoadAbort
, args
.Pass()));
445 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
446 // 'unresponsive' events.
447 void WebViewGuest::RendererResponsive() {
448 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
449 args
->SetInteger(webview::kProcessId
,
450 guest_web_contents()->GetRenderProcessHost()->GetID());
452 new GuestViewBase::Event(webview::kEventResponsive
, args
.Pass()));
455 void WebViewGuest::RendererUnresponsive() {
456 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
457 args
->SetInteger(webview::kProcessId
,
458 guest_web_contents()->GetRenderProcessHost()->GetID());
460 new GuestViewBase::Event(webview::kEventUnresponsive
, args
.Pass()));
463 void WebViewGuest::RequestPermission(
464 BrowserPluginPermissionType permission_type
,
465 const base::DictionaryValue
& request_info
,
466 const PermissionResponseCallback
& callback
,
467 bool allowed_by_default
) {
468 RequestPermissionInternal(permission_type
,
474 void WebViewGuest::Observe(int type
,
475 const content::NotificationSource
& source
,
476 const content::NotificationDetails
& details
) {
478 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
479 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
480 guest_web_contents());
481 if (content::Source
<WebContents
>(source
).ptr() == guest_web_contents())
485 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
486 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
487 guest_web_contents());
488 content::ResourceRedirectDetails
* resource_redirect_details
=
489 content::Details
<content::ResourceRedirectDetails
>(details
).ptr();
491 resource_redirect_details
->resource_type
== ResourceType::MAIN_FRAME
;
492 LoadRedirect(resource_redirect_details
->url
,
493 resource_redirect_details
->new_url
,
498 NOTREACHED() << "Unexpected notification sent.";
503 void WebViewGuest::SetZoom(double zoom_factor
) {
504 double zoom_level
= content::ZoomFactorToZoomLevel(zoom_factor
);
505 guest_web_contents()->SetZoomLevel(zoom_level
);
507 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
508 args
->SetDouble(webview::kOldZoomFactor
, current_zoom_factor_
);
509 args
->SetDouble(webview::kNewZoomFactor
, zoom_factor
);
511 new GuestViewBase::Event(webview::kEventZoomChange
, args
.Pass()));
513 current_zoom_factor_
= zoom_factor
;
516 double WebViewGuest::GetZoom() {
517 return current_zoom_factor_
;
520 void WebViewGuest::Find(
521 const base::string16
& search_text
,
522 const blink::WebFindOptions
& options
,
523 scoped_refptr
<extensions::WebviewFindFunction
> find_function
) {
524 find_helper_
.Find(guest_web_contents(), search_text
, options
, find_function
);
527 void WebViewGuest::StopFinding(content::StopFindAction action
) {
528 find_helper_
.CancelAllFindSessions();
529 guest_web_contents()->StopFinding(action
);
532 void WebViewGuest::Go(int relative_index
) {
533 guest_web_contents()->GetController().GoToOffset(relative_index
);
536 void WebViewGuest::Reload() {
537 // TODO(fsamuel): Don't check for repost because we don't want to show
538 // Chromium's repost warning. We might want to implement a separate API
539 // for registering a callback if a repost is about to happen.
540 guest_web_contents()->GetController().Reload(false);
544 void WebViewGuest::RequestGeolocationPermission(
546 const GURL
& requesting_frame
,
548 const base::Callback
<void(bool)>& callback
) {
549 base::DictionaryValue request_info
;
550 request_info
.Set(guestview::kUrl
,
551 base::Value::CreateStringValue(requesting_frame
.spec()));
552 request_info
.Set(guestview::kUserGesture
,
553 base::Value::CreateBooleanValue(user_gesture
));
555 // It is safe to hold an unretained pointer to WebViewGuest because this
556 // callback is called from WebViewGuest::SetPermission.
557 const PermissionResponseCallback permission_callback
=
558 base::Bind(&WebViewGuest::OnWebViewGeolocationPermissionResponse
,
559 base::Unretained(this),
563 int request_id
= RequestPermissionInternal(
564 static_cast<BrowserPluginPermissionType
>(
565 WEB_VIEW_PERMISSION_TYPE_GEOLOCATION
),
568 false /* allowed_by_default */);
569 bridge_id_to_request_id_map_
[bridge_id
] = request_id
;
572 void WebViewGuest::OnWebViewGeolocationPermissionResponse(
575 const base::Callback
<void(bool)>& callback
,
577 const std::string
& user_input
) {
578 // The <webview> embedder has allowed the permission. We now need to make sure
579 // that the embedder has geolocation permission.
580 RemoveBridgeID(bridge_id
);
582 if (!allow
|| !attached()) {
587 content::GeolocationPermissionContext
* geolocation_context
=
588 browser_context()->GetGeolocationPermissionContext();
590 DCHECK(geolocation_context
);
591 geolocation_context
->RequestGeolocationPermission(
592 embedder_web_contents()->GetRenderProcessHost()->GetID(),
593 embedder_web_contents()->GetRoutingID(),
594 // The geolocation permission request here is not initiated
595 // through WebGeolocationPermissionRequest. We are only interested
596 // in the fact whether the embedder/app has geolocation
597 // permission. Therefore we use an invalid |bridge_id|.
599 embedder_web_contents()->GetLastCommittedURL(),
604 void WebViewGuest::CancelGeolocationPermissionRequest(int bridge_id
) {
605 int request_id
= RemoveBridgeID(bridge_id
);
606 RequestMap::iterator request_itr
=
607 pending_permission_requests_
.find(request_id
);
609 if (request_itr
== pending_permission_requests_
.end())
612 pending_permission_requests_
.erase(request_itr
);
615 void WebViewGuest::OnWebViewMediaPermissionResponse(
616 const content::MediaStreamRequest
& request
,
617 const content::MediaResponseCallback
& callback
,
619 const std::string
& user_input
) {
620 if (!allow
|| !attached()) {
622 callback
.Run(content::MediaStreamDevices(),
623 content::MEDIA_DEVICE_INVALID_STATE
,
624 scoped_ptr
<content::MediaStreamUI
>());
627 if (!embedder_web_contents()->GetDelegate())
630 embedder_web_contents()->GetDelegate()->
631 RequestMediaAccessPermission(embedder_web_contents(), request
, callback
);
634 void WebViewGuest::OnWebViewDownloadPermissionResponse(
635 const base::Callback
<void(bool)>& callback
,
637 const std::string
& user_input
) {
638 callback
.Run(allow
&& attached());
641 void WebViewGuest::OnWebViewPointerLockPermissionResponse(
642 const base::Callback
<void(bool)>& callback
,
644 const std::string
& user_input
) {
645 callback
.Run(allow
&& attached());
648 WebViewGuest::SetPermissionResult
WebViewGuest::SetPermission(
650 PermissionResponseAction action
,
651 const std::string
& user_input
) {
652 RequestMap::iterator request_itr
=
653 pending_permission_requests_
.find(request_id
);
655 if (request_itr
== pending_permission_requests_
.end())
656 return SET_PERMISSION_INVALID
;
658 const PermissionResponseInfo
& info
= request_itr
->second
;
659 bool allow
= (action
== ALLOW
) ||
660 ((action
== DEFAULT
) && info
.allowed_by_default
);
662 info
.callback
.Run(allow
, user_input
);
664 // Only record user initiated (i.e. non-default) actions.
665 if (action
!= DEFAULT
)
666 RecordUserInitiatedUMA(info
, allow
);
668 pending_permission_requests_
.erase(request_itr
);
670 return allow
? SET_PERMISSION_ALLOWED
: SET_PERMISSION_DENIED
;
673 void WebViewGuest::SetUserAgentOverride(
674 const std::string
& user_agent_override
) {
675 is_overriding_user_agent_
= !user_agent_override
.empty();
676 if (is_overriding_user_agent_
) {
677 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
679 guest_web_contents()->SetUserAgentOverride(user_agent_override
);
682 void WebViewGuest::Stop() {
683 guest_web_contents()->Stop();
686 void WebViewGuest::Terminate() {
687 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
688 base::ProcessHandle process_handle
=
689 guest_web_contents()->GetRenderProcessHost()->GetHandle();
691 base::KillProcess(process_handle
, content::RESULT_CODE_KILLED
, false);
694 bool WebViewGuest::ClearData(const base::Time remove_since
,
696 const base::Closure
& callback
) {
697 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
698 content::StoragePartition
* partition
=
699 content::BrowserContext::GetStoragePartition(
700 guest_web_contents()->GetBrowserContext(),
701 guest_web_contents()->GetSiteInstance());
706 partition
->ClearData(
708 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL
,
710 content::StoragePartition::OriginMatcherFunction(),
717 WebViewGuest::~WebViewGuest() {
720 void WebViewGuest::DidCommitProvisionalLoadForFrame(
722 const base::string16
& frame_unique_name
,
725 content::PageTransition transition_type
,
726 content::RenderViewHost
* render_view_host
) {
727 find_helper_
.CancelAllFindSessions();
729 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
730 args
->SetString(guestview::kUrl
, url
.spec());
731 args
->SetBoolean(guestview::kIsTopLevel
, is_main_frame
);
732 args
->SetInteger(webview::kInternalCurrentEntryIndex
,
733 guest_web_contents()->GetController().GetCurrentEntryIndex());
734 args
->SetInteger(webview::kInternalEntryCount
,
735 guest_web_contents()->GetController().GetEntryCount());
736 args
->SetInteger(webview::kInternalProcessId
,
737 guest_web_contents()->GetRenderProcessHost()->GetID());
739 new GuestViewBase::Event(webview::kEventLoadCommit
, args
.Pass()));
741 // Update the current zoom factor for the new page.
742 current_zoom_factor_
= content::ZoomLevelToZoomFactor(
743 guest_web_contents()->GetZoomLevel());
746 chromevox_injected_
= false;
747 main_frame_id_
= frame_id
;
751 void WebViewGuest::DidFailProvisionalLoad(
753 const base::string16
& frame_unique_name
,
755 const GURL
& validated_url
,
757 const base::string16
& error_description
,
758 content::RenderViewHost
* render_view_host
) {
759 // Translate the |error_code| into an error string.
760 std::string error_type
;
761 base::RemoveChars(net::ErrorToString(error_code
), "net::", &error_type
);
762 LoadAbort(is_main_frame
, validated_url
, error_type
);
765 void WebViewGuest::DidStartProvisionalLoadForFrame(
767 int64 parent_frame_id
,
769 const GURL
& validated_url
,
771 bool is_iframe_srcdoc
,
772 content::RenderViewHost
* render_view_host
) {
773 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
774 args
->SetString(guestview::kUrl
, validated_url
.spec());
775 args
->SetBoolean(guestview::kIsTopLevel
, is_main_frame
);
777 new GuestViewBase::Event(webview::kEventLoadStart
, args
.Pass()));
780 void WebViewGuest::DocumentLoadedInFrame(
782 content::RenderViewHost
* render_view_host
) {
783 if (frame_id
== main_frame_id_
)
784 InjectChromeVoxIfNeeded(render_view_host
);
787 void WebViewGuest::DidStopLoading(content::RenderViewHost
* render_view_host
) {
788 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
789 DispatchEvent(new GuestViewBase::Event(webview::kEventLoadStop
, args
.Pass()));
792 void WebViewGuest::WebContentsDestroyed(WebContents
* web_contents
) {
793 // Clean up custom context menu items for this guest.
794 extensions::MenuManager
* menu_manager
= extensions::MenuManager::Get(
795 Profile::FromBrowserContext(browser_context()));
796 menu_manager
->RemoveAllContextItems(extensions::MenuItem::ExtensionKey(
797 embedder_extension_id(), view_instance_id()));
799 RemoveWebViewFromExtensionRendererState(web_contents
);
802 void WebViewGuest::UserAgentOverrideSet(const std::string
& user_agent
) {
803 content::NavigationController
& controller
=
804 guest_web_contents()->GetController();
805 content::NavigationEntry
* entry
= controller
.GetVisibleEntry();
808 entry
->SetIsOverridingUserAgent(!user_agent
.empty());
810 // We cannot reload now because all resource loads are suspended until
812 pending_reload_on_attachment_
= true;
815 guest_web_contents()->GetController().Reload(false);
818 void WebViewGuest::LoadHandlerCalled() {
819 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
821 new GuestViewBase::Event(webview::kEventContentLoad
, args
.Pass()));
824 void WebViewGuest::LoadRedirect(const GURL
& old_url
,
827 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
828 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
829 args
->SetString(webview::kNewURL
, new_url
.spec());
830 args
->SetString(webview::kOldURL
, old_url
.spec());
832 new GuestViewBase::Event(webview::kEventLoadRedirect
, args
.Pass()));
835 void WebViewGuest::AddWebViewToExtensionRendererState() {
836 const GURL
& site_url
= guest_web_contents()->GetSiteInstance()->GetSiteURL();
837 std::string partition_domain
;
838 std::string partition_id
;
840 if (!GetGuestPartitionConfigForSite(
841 site_url
, &partition_domain
, &partition_id
, &in_memory
)) {
845 DCHECK(embedder_extension_id() == partition_domain
);
847 ExtensionRendererState::WebViewInfo webview_info
;
848 webview_info
.embedder_process_id
= embedder_render_process_id();
849 webview_info
.instance_id
= view_instance_id();
850 webview_info
.partition_id
= partition_id
;
851 webview_info
.embedder_extension_id
= embedder_extension_id();
853 content::BrowserThread::PostTask(
854 content::BrowserThread::IO
, FROM_HERE
,
856 &ExtensionRendererState::AddWebView
,
857 base::Unretained(ExtensionRendererState::GetInstance()),
858 guest_web_contents()->GetRenderProcessHost()->GetID(),
859 guest_web_contents()->GetRoutingID(),
864 void WebViewGuest::RemoveWebViewFromExtensionRendererState(
865 WebContents
* web_contents
) {
866 content::BrowserThread::PostTask(
867 content::BrowserThread::IO
, FROM_HERE
,
869 &ExtensionRendererState::RemoveWebView
,
870 base::Unretained(ExtensionRendererState::GetInstance()),
871 web_contents
->GetRenderProcessHost()->GetID(),
872 web_contents
->GetRoutingID()));
875 GURL
WebViewGuest::ResolveURL(const std::string
& src
) {
876 if (!in_extension()) {
881 GURL
default_url(base::StringPrintf("%s://%s/",
882 extensions::kExtensionScheme
,
883 embedder_extension_id().c_str()));
884 return default_url
.Resolve(src
);
887 void WebViewGuest::SizeChanged(const gfx::Size
& old_size
,
888 const gfx::Size
& new_size
) {
889 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
890 args
->SetInteger(webview::kOldHeight
, old_size
.height());
891 args
->SetInteger(webview::kOldWidth
, old_size
.width());
892 args
->SetInteger(webview::kNewHeight
, new_size
.height());
893 args
->SetInteger(webview::kNewWidth
, new_size
.width());
895 new GuestViewBase::Event(webview::kEventSizeChanged
, args
.Pass()));
898 void WebViewGuest::RequestMediaAccessPermission(
899 const content::MediaStreamRequest
& request
,
900 const content::MediaResponseCallback
& callback
) {
901 base::DictionaryValue request_info
;
904 base::Value::CreateStringValue(request
.security_origin
.spec()));
905 RequestPermission(static_cast<BrowserPluginPermissionType
>(
906 WEB_VIEW_PERMISSION_TYPE_MEDIA
),
908 base::Bind(&WebViewGuest::OnWebViewMediaPermissionResponse
,
909 base::Unretained(this),
912 false /* allowed_by_default */);
915 void WebViewGuest::CanDownload(
916 const std::string
& request_method
,
918 const base::Callback
<void(bool)>& callback
) {
919 base::DictionaryValue request_info
;
922 base::Value::CreateStringValue(url
.spec()));
924 static_cast<BrowserPluginPermissionType
>(
925 WEB_VIEW_PERMISSION_TYPE_DOWNLOAD
),
927 base::Bind(&WebViewGuest::OnWebViewDownloadPermissionResponse
,
928 base::Unretained(this),
930 false /* allowed_by_default */);
933 void WebViewGuest::RequestPointerLockPermission(
935 bool last_unlocked_by_target
,
936 const base::Callback
<void(bool)>& callback
) {
937 base::DictionaryValue request_info
;
938 request_info
.Set(guestview::kUserGesture
,
939 base::Value::CreateBooleanValue(user_gesture
));
940 request_info
.Set(webview::kLastUnlockedBySelf
,
941 base::Value::CreateBooleanValue(last_unlocked_by_target
));
942 request_info
.Set(guestview::kUrl
,
943 base::Value::CreateStringValue(
944 guest_web_contents()->GetLastCommittedURL().spec()));
947 static_cast<BrowserPluginPermissionType
>(
948 WEB_VIEW_PERMISSION_TYPE_POINTER_LOCK
),
950 base::Bind(&WebViewGuest::OnWebViewPointerLockPermissionResponse
,
951 base::Unretained(this),
953 false /* allowed_by_default */);
956 content::JavaScriptDialogManager
*
957 WebViewGuest::GetJavaScriptDialogManager() {
958 return &javascript_dialog_helper_
;
961 #if defined(OS_CHROMEOS)
962 void WebViewGuest::OnAccessibilityStatusChanged(
963 const chromeos::AccessibilityStatusEventDetails
& details
) {
964 if (details
.notification_type
== chromeos::ACCESSIBILITY_MANAGER_SHUTDOWN
) {
965 accessibility_subscription_
.reset();
966 } else if (details
.notification_type
==
967 chromeos::ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK
) {
969 InjectChromeVoxIfNeeded(guest_web_contents()->GetRenderViewHost());
971 chromevox_injected_
= false;
976 void WebViewGuest::InjectChromeVoxIfNeeded(
977 content::RenderViewHost
* render_view_host
) {
978 #if defined(OS_CHROMEOS)
979 if (!chromevox_injected_
) {
980 chromeos::AccessibilityManager
* manager
=
981 chromeos::AccessibilityManager::Get();
982 if (manager
&& manager
->IsSpokenFeedbackEnabled()) {
983 manager
->InjectChromeVox(render_view_host
);
984 chromevox_injected_
= true;
990 int WebViewGuest::RemoveBridgeID(int bridge_id
) {
991 std::map
<int, int>::iterator bridge_itr
=
992 bridge_id_to_request_id_map_
.find(bridge_id
);
993 if (bridge_itr
== bridge_id_to_request_id_map_
.end())
994 return webview::kInvalidPermissionRequestID
;
996 int request_id
= bridge_itr
->second
;
997 bridge_id_to_request_id_map_
.erase(bridge_itr
);
1001 int WebViewGuest::RequestPermissionInternal(
1002 BrowserPluginPermissionType permission_type
,
1003 const base::DictionaryValue
& request_info
,
1004 const PermissionResponseCallback
& callback
,
1005 bool allowed_by_default
) {
1006 // If there are too many pending permission requests then reject this request.
1007 if (pending_permission_requests_
.size() >=
1008 webview::kMaxOutstandingPermissionRequests
) {
1009 // Let the stack unwind before we deny the permission request so that
1010 // objects held by the permission request are not destroyed immediately
1011 // after creation. This is to allow those same objects to be accessed again
1012 // in the same scope without fear of use after freeing.
1013 base::MessageLoop::current()->PostTask(
1015 base::Bind(&PermissionResponseCallback::Run
,
1016 base::Owned(new PermissionResponseCallback(callback
)),
1019 return webview::kInvalidPermissionRequestID
;
1022 int request_id
= next_permission_request_id_
++;
1023 pending_permission_requests_
[request_id
] =
1024 PermissionResponseInfo(callback
, permission_type
, allowed_by_default
);
1025 scoped_ptr
<base::DictionaryValue
> args(request_info
.DeepCopy());
1026 args
->SetInteger(webview::kRequestId
, request_id
);
1027 switch (static_cast<int>(permission_type
)) {
1028 case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW
: {
1030 new GuestViewBase::Event(webview::kEventNewWindow
, args
.Pass()));
1033 case WEB_VIEW_PERMISSION_TYPE_JAVASCRIPT_DIALOG
: {
1035 new GuestViewBase::Event(webview::kEventDialog
, args
.Pass()));
1039 args
->SetString(webview::kPermission
,
1040 PermissionTypeToString(permission_type
));
1041 DispatchEvent(new GuestViewBase::Event(webview::kEventPermissionRequest
,
1049 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo()
1050 : permission_type(BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN
),
1051 allowed_by_default(false) {
1054 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo(
1055 const PermissionResponseCallback
& callback
,
1056 BrowserPluginPermissionType permission_type
,
1057 bool allowed_by_default
)
1058 : callback(callback
),
1059 permission_type(permission_type
),
1060 allowed_by_default(allowed_by_default
) {
1063 WebViewGuest::PermissionResponseInfo::~PermissionResponseInfo() {