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 "extensions/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 "base/strings/utf_string_conversions.h"
10 #include "content/public/browser/browser_context.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/child_process_security_policy.h"
13 #include "content/public/browser/native_web_keyboard_event.h"
14 #include "content/public/browser/navigation_entry.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/resource_request_details.h"
23 #include "content/public/browser/site_instance.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/browser/user_metrics.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/common/media_stream_request.h"
29 #include "content/public/common/page_zoom.h"
30 #include "content/public/common/result_codes.h"
31 #include "content/public/common/stop_find_action.h"
32 #include "content/public/common/url_constants.h"
33 #include "extensions/browser/api/extensions_api_client.h"
34 #include "extensions/browser/api/web_request/web_request_api.h"
35 #include "extensions/browser/api/web_view/web_view_internal_api.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/browser/guest_view/guest_view_manager.h"
38 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
39 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
40 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
41 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/extension_messages.h"
44 #include "extensions/common/guest_view/guest_view_constants.h"
45 #include "extensions/strings/grit/extensions_strings.h"
46 #include "ipc/ipc_message_macros.h"
47 #include "net/base/escape.h"
48 #include "net/base/net_errors.h"
49 #include "ui/base/models/simple_menu_model.h"
51 using base::UserMetricsAction
;
52 using content::RenderFrameHost
;
53 using content::ResourceType
;
54 using content::WebContents
;
56 namespace extensions
{
60 std::string
WindowOpenDispositionToString(
61 WindowOpenDisposition window_open_disposition
) {
62 switch (window_open_disposition
) {
66 return "save_to_disk";
69 case NEW_BACKGROUND_TAB
:
70 return "new_background_tab";
71 case NEW_FOREGROUND_TAB
:
72 return "new_foreground_tab";
78 NOTREACHED() << "Unknown Window Open Disposition";
83 static std::string
TerminationStatusToString(base::TerminationStatus status
) {
85 case base::TERMINATION_STATUS_NORMAL_TERMINATION
:
87 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
88 case base::TERMINATION_STATUS_STILL_RUNNING
:
90 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
92 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
94 case base::TERMINATION_STATUS_MAX_ENUM
:
97 NOTREACHED() << "Unknown Termination Status.";
101 std::string
GetStoragePartitionIdFromSiteURL(const GURL
& site_url
) {
102 const std::string
& partition_id
= site_url
.query();
103 bool persist_storage
= site_url
.path().find("persist") != std::string::npos
;
104 return (persist_storage
? webview::kPersistPrefix
: "") + partition_id
;
107 void ParsePartitionParam(const base::DictionaryValue
& create_params
,
108 std::string
* storage_partition_id
,
109 bool* persist_storage
) {
110 std::string partition_str
;
111 if (!create_params
.GetString(webview::kStoragePartitionId
, &partition_str
)) {
115 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
116 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
117 // remove the prefix without splicing in the middle of a multi-byte codepoint.
118 // We can use the rest of the string as UTF-8 encoded one.
119 if (StartsWithASCII(partition_str
, "persist:", true)) {
120 size_t index
= partition_str
.find(":");
121 CHECK(index
!= std::string::npos
);
122 // It is safe to do index + 1, since we tested for the full prefix above.
123 *storage_partition_id
= partition_str
.substr(index
+ 1);
125 if (storage_partition_id
->empty()) {
126 // TODO(lazyboy): Better way to deal with this error.
129 *persist_storage
= true;
131 *storage_partition_id
= partition_str
;
132 *persist_storage
= false;
136 void RemoveWebViewEventListenersOnIOThread(
138 const std::string
& extension_id
,
139 int embedder_process_id
,
140 int view_instance_id
) {
141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
142 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
152 GuestViewBase
* WebViewGuest::Create(content::BrowserContext
* browser_context
,
153 int guest_instance_id
) {
154 return new WebViewGuest(browser_context
, guest_instance_id
);
158 bool WebViewGuest::GetGuestPartitionConfigForSite(
160 std::string
* partition_domain
,
161 std::string
* partition_name
,
163 if (!site
.SchemeIs(content::kGuestScheme
))
166 // Since guest URLs are only used for packaged apps, there must be an app
168 CHECK(site
.has_host());
169 *partition_domain
= site
.host();
170 // Since persistence is optional, the path must either be empty or the
172 *in_memory
= (site
.path() != "/persist");
173 // The partition name is user supplied value, which we have encoded when the
174 // URL was created, so it needs to be decoded.
176 net::UnescapeURLComponent(site
.query(), net::UnescapeRule::NORMAL
);
181 const char WebViewGuest::Type
[] = "webview";
184 int WebViewGuest::GetViewInstanceId(WebContents
* contents
) {
185 WebViewGuest
* guest
= FromWebContents(contents
);
187 return guestview::kInstanceIDNone
;
189 return guest
->view_instance_id();
192 const char* WebViewGuest::GetAPINamespace() const {
193 return webview::kAPINamespace
;
196 int WebViewGuest::GetTaskPrefix() const {
197 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX
;
200 void WebViewGuest::CreateWebContents(
201 const std::string
& embedder_extension_id
,
202 int embedder_render_process_id
,
203 const GURL
& embedder_site_url
,
204 const base::DictionaryValue
& create_params
,
205 const WebContentsCreatedCallback
& callback
) {
206 content::RenderProcessHost
* embedder_render_process_host
=
207 content::RenderProcessHost::FromID(embedder_render_process_id
);
208 std::string storage_partition_id
;
209 bool persist_storage
= false;
210 std::string storage_partition_string
;
211 ParsePartitionParam(create_params
, &storage_partition_id
, &persist_storage
);
212 // Validate that the partition id coming from the renderer is valid UTF-8,
213 // since we depend on this in other parts of the code, such as FilePath
214 // creation. If the validation fails, treat it as a bad message and kill the
216 if (!base::IsStringUTF8(storage_partition_id
)) {
217 content::RecordAction(
218 base::UserMetricsAction("BadMessageTerminate_BPGM"));
220 embedder_render_process_host
->GetHandle(),
221 content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
225 std::string url_encoded_partition
= net::EscapeQueryParamValue(
226 storage_partition_id
, false);
227 // The SiteInstance of a given webview tag is based on the fact that it's
228 // a guest process in addition to which platform application or which WebUI
229 // page the tag belongs to and what storage partition is in use, rather than
230 // the URL that the tag is being navigated to.
231 std::string partition_domain
;
232 if (embedder_extension_id
.empty()) {
233 DCHECK(content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
234 embedder_render_process_id
));
235 partition_domain
= embedder_site_url
.host();
237 partition_domain
= embedder_extension_id
;
239 GURL
guest_site(base::StringPrintf("%s://%s/%s?%s",
240 content::kGuestScheme
,
241 partition_domain
.c_str(),
242 persist_storage
? "persist" : "",
243 url_encoded_partition
.c_str()));
245 // If we already have a webview tag in the same app using the same storage
246 // partition, we should use the same SiteInstance so the existing tag and
247 // the new tag can script each other.
248 GuestViewManager
* guest_view_manager
=
249 GuestViewManager::FromBrowserContext(
250 embedder_render_process_host
->GetBrowserContext());
251 content::SiteInstance
* guest_site_instance
=
252 guest_view_manager
->GetGuestSiteInstance(guest_site
);
253 if (!guest_site_instance
) {
254 // Create the SiteInstance in a new BrowsingInstance, which will ensure
255 // that webview tags are also not allowed to send messages across
256 // different partitions.
257 guest_site_instance
= content::SiteInstance::CreateForURL(
258 embedder_render_process_host
->GetBrowserContext(), guest_site
);
260 WebContents::CreateParams
params(
261 embedder_render_process_host
->GetBrowserContext(),
262 guest_site_instance
);
263 params
.guest_delegate
= this;
264 callback
.Run(WebContents::Create(params
));
267 void WebViewGuest::DidAttachToEmbedder() {
271 if (attach_params()->GetString(webview::kAttributeName
, &name
)) {
272 // If the guest window's name is empty, then the WebView tag's name is
273 // assigned. Otherwise, the guest window's name takes precedence over the
274 // WebView tag's name.
278 ReportFrameNameChange(name_
);
280 std::string user_agent_override
;
281 if (attach_params()->GetString(webview::kParameterUserAgentOverride
,
282 &user_agent_override
)) {
283 SetUserAgentOverride(user_agent_override
);
285 SetUserAgentOverride("");
288 bool is_pending_new_window
= false;
290 // We need to do a navigation here if the target URL has changed between
291 // the time the WebContents was created and the time it was attached.
292 // We also need to do an initial navigation if a RenderView was never
293 // created for the new window in cases where there is no referrer.
294 PendingWindowMap::iterator it
=
295 GetOpener()->pending_new_windows_
.find(this);
296 if (it
!= GetOpener()->pending_new_windows_
.end()) {
297 const NewWindowInfo
& new_window_info
= it
->second
;
298 if (new_window_info
.changed
|| !web_contents()->HasOpener())
299 NavigateGuest(new_window_info
.url
.spec(), false /* force_navigation */);
301 // Once a new guest is attached to the DOM of the embedder page, then the
302 // lifetime of the new guest is no longer managed by the opener guest.
303 GetOpener()->pending_new_windows_
.erase(this);
305 is_pending_new_window
= true;
309 // Only read the src attribute if this is not a New Window API flow.
310 if (!is_pending_new_window
) {
312 if (attach_params()->GetString(webview::kAttributeSrc
, &src
) &&
314 NavigateGuest(src
, false /* force_navigation */);
318 bool allow_transparency
= false;
319 attach_params()->GetBoolean(webview::kAttributeAllowTransparency
,
320 &allow_transparency
);
321 // We need to set the background opaque flag after navigation to ensure that
322 // there is a RenderWidgetHostView available.
323 SetAllowTransparency(allow_transparency
);
325 if (web_view_guest_delegate_
)
326 web_view_guest_delegate_
->OnDidAttachToEmbedder();
329 void WebViewGuest::DidInitialize() {
330 script_executor_
.reset(
331 new ScriptExecutor(web_contents(), &script_observers_
));
333 notification_registrar_
.Add(this,
334 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
335 content::Source
<WebContents
>(web_contents()));
337 notification_registrar_
.Add(this,
338 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
339 content::Source
<WebContents
>(web_contents()));
341 if (web_view_guest_delegate_
)
342 web_view_guest_delegate_
->OnDidInitialize();
343 AttachWebViewHelpers(web_contents());
346 void WebViewGuest::AttachWebViewHelpers(WebContents
* contents
) {
347 if (web_view_guest_delegate_
)
348 web_view_guest_delegate_
->OnAttachWebViewHelpers(contents
);
349 web_view_permission_helper_
.reset(new WebViewPermissionHelper(this));
352 void WebViewGuest::DidStopLoading() {
353 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
354 DispatchEventToEmbedder(
355 new GuestViewBase::Event(webview::kEventLoadStop
, args
.Pass()));
358 void WebViewGuest::EmbedderWillBeDestroyed() {
359 if (web_view_guest_delegate_
)
360 web_view_guest_delegate_
->OnEmbedderWillBeDestroyed();
362 content::BrowserThread::PostTask(
363 content::BrowserThread::IO
,
366 &RemoveWebViewEventListenersOnIOThread
,
368 embedder_extension_id(),
369 embedder_render_process_id(),
370 view_instance_id()));
373 void WebViewGuest::GuestDestroyed() {
374 // Clean up custom context menu items for this guest.
375 if (web_view_guest_delegate_
)
376 web_view_guest_delegate_
->OnGuestDestroyed();
377 RemoveWebViewStateFromIOThread(web_contents());
380 void WebViewGuest::GuestReady() {
381 // The guest RenderView should always live in an isolated guest process.
382 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
383 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_
));
385 // We don't want to accidentally set the opacity of an interstitial page.
386 // WebContents::GetRenderWidgetHostView will return the RWHV of an
387 // interstitial page if one is showing at this time. We only want opacity
388 // to apply to web pages.
391 ->GetRenderViewHost()
393 ->SetBackgroundColorToDefault();
395 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
396 SK_ColorTRANSPARENT
);
400 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size
& old_size
,
401 const gfx::Size
& new_size
) {
402 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
403 args
->SetInteger(webview::kOldHeight
, old_size
.height());
404 args
->SetInteger(webview::kOldWidth
, old_size
.width());
405 args
->SetInteger(webview::kNewHeight
, new_size
.height());
406 args
->SetInteger(webview::kNewWidth
, new_size
.width());
407 DispatchEventToEmbedder(
408 new GuestViewBase::Event(webview::kEventSizeChanged
, args
.Pass()));
411 bool WebViewGuest::IsAutoSizeSupported() const {
415 bool WebViewGuest::IsDragAndDropEnabled() const {
419 void WebViewGuest::WillDestroy() {
420 if (!attached() && GetOpener())
421 GetOpener()->pending_new_windows_
.erase(this);
424 bool WebViewGuest::AddMessageToConsole(WebContents
* source
,
426 const base::string16
& message
,
428 const base::string16
& source_id
) {
429 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
430 // Log levels are from base/logging.h: LogSeverity.
431 args
->SetInteger(webview::kLevel
, level
);
432 args
->SetString(webview::kMessage
, message
);
433 args
->SetInteger(webview::kLine
, line_no
);
434 args
->SetString(webview::kSourceId
, source_id
);
435 DispatchEventToEmbedder(
436 new GuestViewBase::Event(webview::kEventConsoleMessage
, args
.Pass()));
440 void WebViewGuest::CloseContents(WebContents
* source
) {
441 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
442 DispatchEventToEmbedder(
443 new GuestViewBase::Event(webview::kEventClose
, args
.Pass()));
446 void WebViewGuest::FindReply(WebContents
* source
,
448 int number_of_matches
,
449 const gfx::Rect
& selection_rect
,
450 int active_match_ordinal
,
452 find_helper_
.FindReply(request_id
,
455 active_match_ordinal
,
459 bool WebViewGuest::HandleContextMenu(
460 const content::ContextMenuParams
& params
) {
461 if (!web_view_guest_delegate_
)
463 return web_view_guest_delegate_
->HandleContextMenu(params
);
466 void WebViewGuest::HandleKeyboardEvent(
468 const content::NativeWebKeyboardEvent
& event
) {
472 if (HandleKeyboardShortcuts(event
))
475 // Send the unhandled keyboard events back to the embedder to reprocess them.
476 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
477 // events because the guest may be arbitrarily delayed when responding to
478 // keyboard events. In that time, the embedder may have received and processed
479 // additional key events. This needs to be fixed as soon as possible.
480 // See http://crbug.com/229882.
481 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
482 web_contents(), event
);
485 void WebViewGuest::LoadProgressChanged(content::WebContents
* source
,
487 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
488 args
->SetString(guestview::kUrl
, web_contents()->GetURL().spec());
489 args
->SetDouble(webview::kProgress
, progress
);
490 DispatchEventToEmbedder(
491 new GuestViewBase::Event(webview::kEventLoadProgress
, args
.Pass()));
494 void WebViewGuest::LoadAbort(bool is_top_level
,
496 const std::string
& error_type
) {
497 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
498 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
499 args
->SetString(guestview::kUrl
, url
.possibly_invalid_spec());
500 args
->SetString(guestview::kReason
, error_type
);
501 DispatchEventToEmbedder(
502 new GuestViewBase::Event(webview::kEventLoadAbort
, args
.Pass()));
505 void WebViewGuest::OnFrameNameChanged(bool is_top_level
,
506 const std::string
& name
) {
513 ReportFrameNameChange(name
);
516 void WebViewGuest::CreateNewGuestWebViewWindow(
517 const content::OpenURLParams
& params
) {
518 GuestViewManager
* guest_manager
=
519 GuestViewManager::FromBrowserContext(browser_context());
520 // Set the attach params to use the same partition as the opener.
521 // We pull the partition information from the site's URL, which is of the
522 // form guest://site/{persist}?{partition_name}.
523 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
524 const std::string storage_partition_id
=
525 GetStoragePartitionIdFromSiteURL(site_url
);
526 base::DictionaryValue create_params
;
527 create_params
.SetString(webview::kStoragePartitionId
, storage_partition_id
);
529 guest_manager
->CreateGuest(WebViewGuest::Type
,
530 embedder_extension_id(),
531 embedder_web_contents(),
533 base::Bind(&WebViewGuest::NewGuestWebViewCallback
,
534 weak_ptr_factory_
.GetWeakPtr(),
538 void WebViewGuest::NewGuestWebViewCallback(
539 const content::OpenURLParams
& params
,
540 content::WebContents
* guest_web_contents
) {
541 WebViewGuest
* new_guest
= WebViewGuest::FromWebContents(guest_web_contents
);
542 new_guest
->SetOpener(this);
544 // Take ownership of |new_guest|.
545 pending_new_windows_
.insert(
546 std::make_pair(new_guest
, NewWindowInfo(params
.url
, std::string())));
548 // Request permission to show the new window.
549 RequestNewWindowPermission(params
.disposition
,
552 new_guest
->web_contents());
555 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
556 // 'unresponsive' events.
557 void WebViewGuest::RendererResponsive(content::WebContents
* source
) {
558 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
559 args
->SetInteger(webview::kProcessId
,
560 web_contents()->GetRenderProcessHost()->GetID());
561 DispatchEventToEmbedder(
562 new GuestViewBase::Event(webview::kEventResponsive
, args
.Pass()));
565 void WebViewGuest::RendererUnresponsive(content::WebContents
* source
) {
566 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
567 args
->SetInteger(webview::kProcessId
,
568 web_contents()->GetRenderProcessHost()->GetID());
569 DispatchEventToEmbedder(
570 new GuestViewBase::Event(webview::kEventUnresponsive
, args
.Pass()));
573 void WebViewGuest::Observe(int type
,
574 const content::NotificationSource
& source
,
575 const content::NotificationDetails
& details
) {
577 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
578 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
579 if (content::Source
<WebContents
>(source
).ptr() == web_contents())
583 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
584 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
585 content::ResourceRedirectDetails
* resource_redirect_details
=
586 content::Details
<content::ResourceRedirectDetails
>(details
).ptr();
587 bool is_top_level
= resource_redirect_details
->resource_type
==
588 content::RESOURCE_TYPE_MAIN_FRAME
;
589 LoadRedirect(resource_redirect_details
->url
,
590 resource_redirect_details
->new_url
,
595 NOTREACHED() << "Unexpected notification sent.";
600 double WebViewGuest::GetZoom() {
601 if (!web_view_guest_delegate_
)
603 return web_view_guest_delegate_
->GetZoom();
606 void WebViewGuest::StartFinding(
607 const base::string16
& search_text
,
608 const blink::WebFindOptions
& options
,
609 scoped_refptr
<WebViewInternalFindFunction
> find_function
) {
610 find_helper_
.Find(web_contents(), search_text
, options
, find_function
);
613 void WebViewGuest::StopFinding(content::StopFindAction action
) {
614 find_helper_
.CancelAllFindSessions();
615 web_contents()->StopFinding(action
);
618 bool WebViewGuest::Go(int relative_index
) {
619 content::NavigationController
& controller
= web_contents()->GetController();
620 if (!controller
.CanGoToOffset(relative_index
))
623 controller
.GoToOffset(relative_index
);
627 void WebViewGuest::Reload() {
628 // TODO(fsamuel): Don't check for repost because we don't want to show
629 // Chromium's repost warning. We might want to implement a separate API
630 // for registering a callback if a repost is about to happen.
631 web_contents()->GetController().Reload(false);
634 void WebViewGuest::SetUserAgentOverride(
635 const std::string
& user_agent_override
) {
638 is_overriding_user_agent_
= !user_agent_override
.empty();
639 if (is_overriding_user_agent_
) {
640 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
642 web_contents()->SetUserAgentOverride(user_agent_override
);
645 void WebViewGuest::Stop() {
646 web_contents()->Stop();
649 void WebViewGuest::Terminate() {
650 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
651 base::ProcessHandle process_handle
=
652 web_contents()->GetRenderProcessHost()->GetHandle();
654 base::KillProcess(process_handle
, content::RESULT_CODE_KILLED
, false);
657 bool WebViewGuest::ClearData(const base::Time remove_since
,
659 const base::Closure
& callback
) {
660 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
661 content::StoragePartition
* partition
=
662 content::BrowserContext::GetStoragePartition(
663 web_contents()->GetBrowserContext(),
664 web_contents()->GetSiteInstance());
669 partition
->ClearData(
671 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL
,
673 content::StoragePartition::OriginMatcherFunction(),
680 WebViewGuest::WebViewGuest(content::BrowserContext
* browser_context
,
681 int guest_instance_id
)
682 : GuestView
<WebViewGuest
>(browser_context
, guest_instance_id
),
684 is_overriding_user_agent_(false),
686 javascript_dialog_helper_(this),
687 weak_ptr_factory_(this) {
688 web_view_guest_delegate_
.reset(
689 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
692 WebViewGuest::~WebViewGuest() {
695 void WebViewGuest::DidCommitProvisionalLoadForFrame(
696 content::RenderFrameHost
* render_frame_host
,
698 ui::PageTransition transition_type
) {
699 if (!render_frame_host
->GetParent())
701 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
702 args
->SetString(guestview::kUrl
, url
.spec());
703 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
704 args
->SetString(webview::kInternalBaseURLForDataURL
,
707 .GetLastCommittedEntry()
708 ->GetBaseURLForDataURL()
710 args
->SetInteger(webview::kInternalCurrentEntryIndex
,
711 web_contents()->GetController().GetCurrentEntryIndex());
712 args
->SetInteger(webview::kInternalEntryCount
,
713 web_contents()->GetController().GetEntryCount());
714 args
->SetInteger(webview::kInternalProcessId
,
715 web_contents()->GetRenderProcessHost()->GetID());
716 DispatchEventToEmbedder(
717 new GuestViewBase::Event(webview::kEventLoadCommit
, args
.Pass()));
719 find_helper_
.CancelAllFindSessions();
720 if (web_view_guest_delegate_
) {
721 web_view_guest_delegate_
->OnDidCommitProvisionalLoadForFrame(
722 !render_frame_host
->GetParent());
726 void WebViewGuest::DidFailProvisionalLoad(
727 content::RenderFrameHost
* render_frame_host
,
728 const GURL
& validated_url
,
730 const base::string16
& error_description
) {
731 LoadAbort(!render_frame_host
->GetParent(), validated_url
,
732 net::ErrorToShortString(error_code
));
735 void WebViewGuest::DidStartProvisionalLoadForFrame(
736 content::RenderFrameHost
* render_frame_host
,
737 const GURL
& validated_url
,
739 bool is_iframe_srcdoc
) {
740 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
741 args
->SetString(guestview::kUrl
, validated_url
.spec());
742 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
743 DispatchEventToEmbedder(
744 new GuestViewBase::Event(webview::kEventLoadStart
, args
.Pass()));
747 void WebViewGuest::DocumentLoadedInFrame(
748 content::RenderFrameHost
* render_frame_host
) {
749 if (web_view_guest_delegate_
)
750 web_view_guest_delegate_
->OnDocumentLoadedInFrame(render_frame_host
);
753 bool WebViewGuest::OnMessageReceived(const IPC::Message
& message
,
754 RenderFrameHost
* render_frame_host
) {
756 IPC_BEGIN_MESSAGE_MAP(WebViewGuest
, message
)
757 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged
, OnFrameNameChanged
)
758 IPC_MESSAGE_UNHANDLED(handled
= false)
759 IPC_END_MESSAGE_MAP()
763 void WebViewGuest::RenderProcessGone(base::TerminationStatus status
) {
764 // Cancel all find sessions in progress.
765 find_helper_
.CancelAllFindSessions();
767 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
768 args
->SetInteger(webview::kProcessId
,
769 web_contents()->GetRenderProcessHost()->GetID());
770 args
->SetString(webview::kReason
, TerminationStatusToString(status
));
771 DispatchEventToEmbedder(
772 new GuestViewBase::Event(webview::kEventExit
, args
.Pass()));
775 void WebViewGuest::UserAgentOverrideSet(const std::string
& user_agent
) {
778 content::NavigationController
& controller
= web_contents()->GetController();
779 content::NavigationEntry
* entry
= controller
.GetVisibleEntry();
782 entry
->SetIsOverridingUserAgent(!user_agent
.empty());
783 web_contents()->GetController().Reload(false);
786 void WebViewGuest::ReportFrameNameChange(const std::string
& name
) {
788 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
789 args
->SetString(webview::kName
, name
);
790 DispatchEventToEmbedder(
791 new GuestViewBase::Event(webview::kEventFrameNameChanged
, args
.Pass()));
794 void WebViewGuest::LoadHandlerCalled() {
795 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
796 DispatchEventToEmbedder(
797 new GuestViewBase::Event(webview::kEventContentLoad
, args
.Pass()));
800 void WebViewGuest::LoadRedirect(const GURL
& old_url
,
803 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
804 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
805 args
->SetString(webview::kNewURL
, new_url
.spec());
806 args
->SetString(webview::kOldURL
, old_url
.spec());
807 DispatchEventToEmbedder(
808 new GuestViewBase::Event(webview::kEventLoadRedirect
, args
.Pass()));
811 void WebViewGuest::PushWebViewStateToIOThread() {
812 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
813 std::string partition_domain
;
814 std::string partition_id
;
816 if (!GetGuestPartitionConfigForSite(
817 site_url
, &partition_domain
, &partition_id
, &in_memory
)) {
822 WebViewRendererState::WebViewInfo web_view_info
;
823 web_view_info
.embedder_process_id
= embedder_render_process_id();
824 web_view_info
.instance_id
= view_instance_id();
825 web_view_info
.partition_id
= partition_id
;
826 web_view_info
.embedder_extension_id
= embedder_extension_id();
828 content::BrowserThread::PostTask(
829 content::BrowserThread::IO
,
831 base::Bind(&WebViewRendererState::AddGuest
,
832 base::Unretained(WebViewRendererState::GetInstance()),
833 web_contents()->GetRenderProcessHost()->GetID(),
834 web_contents()->GetRoutingID(),
839 void WebViewGuest::RemoveWebViewStateFromIOThread(
840 WebContents
* web_contents
) {
841 content::BrowserThread::PostTask(
842 content::BrowserThread::IO
, FROM_HERE
,
844 &WebViewRendererState::RemoveGuest
,
845 base::Unretained(WebViewRendererState::GetInstance()),
846 web_contents
->GetRenderProcessHost()->GetID(),
847 web_contents
->GetRoutingID()));
850 content::WebContents
* WebViewGuest::CreateNewGuestWindow(
851 const content::WebContents::CreateParams
& create_params
) {
852 GuestViewManager
* guest_manager
=
853 GuestViewManager::FromBrowserContext(browser_context());
854 return guest_manager
->CreateGuestWithWebContentsParams(
856 embedder_extension_id(),
857 embedder_web_contents(),
861 void WebViewGuest::RequestMediaAccessPermission(
862 content::WebContents
* source
,
863 const content::MediaStreamRequest
& request
,
864 const content::MediaResponseCallback
& callback
) {
865 web_view_permission_helper_
->RequestMediaAccessPermission(source
,
870 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents
* source
,
871 const GURL
& security_origin
,
872 content::MediaStreamType type
) {
873 return web_view_permission_helper_
->CheckMediaAccessPermission(
874 source
, security_origin
, type
);
877 void WebViewGuest::CanDownload(
878 content::RenderViewHost
* render_view_host
,
880 const std::string
& request_method
,
881 const base::Callback
<void(bool)>& callback
) {
882 web_view_permission_helper_
->CanDownload(render_view_host
,
888 void WebViewGuest::RequestPointerLockPermission(
890 bool last_unlocked_by_target
,
891 const base::Callback
<void(bool)>& callback
) {
892 web_view_permission_helper_
->RequestPointerLockPermission(
894 last_unlocked_by_target
,
898 void WebViewGuest::WillAttachToEmbedder() {
899 // We must install the mapping from guests to WebViews prior to resuming
900 // suspended resource loads so that the WebRequest API will catch resource
902 PushWebViewStateToIOThread();
905 content::JavaScriptDialogManager
* WebViewGuest::GetJavaScriptDialogManager(
906 WebContents
* source
) {
907 return &javascript_dialog_helper_
;
910 content::ColorChooser
* WebViewGuest::OpenColorChooser(
911 WebContents
* web_contents
,
913 const std::vector
<content::ColorSuggestion
>& suggestions
) {
914 if (!attached() || !embedder_web_contents()->GetDelegate())
916 return embedder_web_contents()->GetDelegate()->OpenColorChooser(
917 web_contents
, color
, suggestions
);
920 void WebViewGuest::NavigateGuest(const std::string
& src
,
921 bool force_navigation
) {
928 GURL url
= ResolveURL(src
);
930 // Do not allow navigating a guest to schemes other than known safe schemes.
931 // This will block the embedder trying to load unwanted schemes, e.g.
932 // chrome://settings.
933 bool scheme_is_blocked
=
934 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
936 !url
.SchemeIs(url::kAboutScheme
)) ||
937 url
.SchemeIs(url::kJavaScriptScheme
);
938 if (scheme_is_blocked
|| !url
.is_valid()) {
939 LoadAbort(true /* is_top_level */, url
,
940 net::ErrorToShortString(net::ERR_ABORTED
));
943 if (!force_navigation
&& (src_
== url
))
946 GURL
validated_url(url
);
947 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url
);
948 // As guests do not swap processes on navigation, only navigations to
949 // normal web URLs are supported. No protocol handlers are installed for
950 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
951 // can be granted to the guest process.
952 LoadURLWithParams(validated_url
,
954 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
958 bool WebViewGuest::HandleKeyboardShortcuts(
959 const content::NativeWebKeyboardEvent
& event
) {
960 if (event
.type
!= blink::WebInputEvent::RawKeyDown
)
963 // If the user hits the escape key without any modifiers then unlock the
964 // mouse if necessary.
965 if ((event
.windowsKeyCode
== ui::VKEY_ESCAPE
) &&
966 !(event
.modifiers
& blink::WebInputEvent::InputModifiers
)) {
967 return web_contents()->GotResponseToLockMouseRequest(false);
970 #if defined(OS_MACOSX)
971 if (event
.modifiers
!= blink::WebInputEvent::MetaKey
)
974 if (event
.windowsKeyCode
== ui::VKEY_OEM_4
) {
979 if (event
.windowsKeyCode
== ui::VKEY_OEM_6
) {
984 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_BACK
) {
989 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_FORWARD
) {
998 void WebViewGuest::SetUpAutoSize() {
999 // Read the autosize parameters passed in from the embedder.
1000 bool auto_size_enabled
= false;
1001 attach_params()->GetBoolean(webview::kAttributeAutoSize
, &auto_size_enabled
);
1005 attach_params()->GetInteger(webview::kAttributeMaxHeight
, &max_height
);
1006 attach_params()->GetInteger(webview::kAttributeMaxWidth
, &max_width
);
1010 attach_params()->GetInteger(webview::kAttributeMinHeight
, &min_height
);
1011 attach_params()->GetInteger(webview::kAttributeMinWidth
, &min_width
);
1013 // Call SetAutoSize to apply all the appropriate validation and clipping of
1015 SetAutoSize(auto_size_enabled
,
1016 gfx::Size(min_width
, min_height
),
1017 gfx::Size(max_width
, max_height
));
1020 void WebViewGuest::ShowContextMenu(
1022 const WebViewGuestDelegate::MenuItemVector
* items
) {
1023 if (web_view_guest_delegate_
)
1024 web_view_guest_delegate_
->OnShowContextMenu(request_id
, items
);
1027 void WebViewGuest::SetName(const std::string
& name
) {
1032 Send(new ExtensionMsg_SetFrameName(routing_id(), name_
));
1035 void WebViewGuest::SetZoom(double zoom_factor
) {
1036 if (web_view_guest_delegate_
)
1037 web_view_guest_delegate_
->OnSetZoom(zoom_factor
);
1040 void WebViewGuest::SetAllowTransparency(bool allow
) {
1041 if (guest_opaque_
!= allow
)
1044 guest_opaque_
= !allow
;
1045 if (!web_contents()->GetRenderViewHost()->GetView())
1048 if (guest_opaque_
) {
1050 ->GetRenderViewHost()
1052 ->SetBackgroundColorToDefault();
1054 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1055 SK_ColorTRANSPARENT
);
1059 bool WebViewGuest::LoadDataWithBaseURL(const std::string
& data_url
,
1060 const std::string
& base_url
,
1061 const std::string
& virtual_url
,
1062 std::string
* error
) {
1063 // Make GURLs from URLs.
1064 const GURL data_gurl
= GURL(data_url
);
1065 const GURL base_gurl
= GURL(base_url
);
1066 const GURL virtual_gurl
= GURL(virtual_url
);
1068 // Check that the provided URLs are valid.
1069 // |data_url| must be a valid data URL.
1070 if (!data_gurl
.is_valid() || !data_gurl
.SchemeIs(url::kDataScheme
)) {
1071 base::SStringPrintf(
1072 error
, webview::kAPILoadDataInvalidDataURL
, data_url
.c_str());
1075 // |base_url| must be a valid URL.
1076 if (!base_gurl
.is_valid()) {
1077 base::SStringPrintf(
1078 error
, webview::kAPILoadDataInvalidBaseURL
, base_url
.c_str());
1081 // |virtual_url| must be a valid URL.
1082 if (!virtual_gurl
.is_valid()) {
1083 base::SStringPrintf(
1084 error
, webview::kAPILoadDataInvalidVirtualURL
, virtual_url
.c_str());
1088 // Set up the parameters to load |data_url| with the specified |base_url|.
1089 content::NavigationController::LoadURLParams
load_params(data_gurl
);
1090 load_params
.load_type
= content::NavigationController::LOAD_TYPE_DATA
;
1091 load_params
.base_url_for_data_url
= base_gurl
;
1092 load_params
.virtual_url_for_data_url
= virtual_gurl
;
1093 load_params
.override_user_agent
=
1094 content::NavigationController::UA_OVERRIDE_INHERIT
;
1096 // Navigate to the data URL.
1097 web_contents()->GetController().LoadURLWithParams(load_params
);
1102 void WebViewGuest::AddNewContents(content::WebContents
* source
,
1103 content::WebContents
* new_contents
,
1104 WindowOpenDisposition disposition
,
1105 const gfx::Rect
& initial_pos
,
1107 bool* was_blocked
) {
1109 *was_blocked
= false;
1110 RequestNewWindowPermission(disposition
,
1116 content::WebContents
* WebViewGuest::OpenURLFromTab(
1117 content::WebContents
* source
,
1118 const content::OpenURLParams
& params
) {
1119 // If the guest wishes to navigate away prior to attachment then we save the
1120 // navigation to perform upon attachment. Navigation initializes a lot of
1121 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1122 // Navigation also resumes resource loading which we don't want to allow
1123 // until attachment.
1125 WebViewGuest
* opener
= GetOpener();
1126 PendingWindowMap::iterator it
=
1127 opener
->pending_new_windows_
.find(this);
1128 if (it
== opener
->pending_new_windows_
.end())
1130 const NewWindowInfo
& info
= it
->second
;
1131 NewWindowInfo
new_window_info(params
.url
, info
.name
);
1132 new_window_info
.changed
= new_window_info
.url
!= info
.url
;
1133 it
->second
= new_window_info
;
1136 if (params
.disposition
== CURRENT_TAB
) {
1137 // This can happen for cross-site redirects.
1138 LoadURLWithParams(params
.url
, params
.referrer
, params
.transition
, source
);
1142 CreateNewGuestWebViewWindow(params
);
1146 void WebViewGuest::WebContentsCreated(WebContents
* source_contents
,
1147 int opener_render_frame_id
,
1148 const base::string16
& frame_name
,
1149 const GURL
& target_url
,
1150 content::WebContents
* new_contents
) {
1151 WebViewGuest
* guest
= WebViewGuest::FromWebContents(new_contents
);
1153 guest
->SetOpener(this);
1154 std::string guest_name
= base::UTF16ToUTF8(frame_name
);
1155 guest
->name_
= guest_name
;
1156 pending_new_windows_
.insert(
1157 std::make_pair(guest
, NewWindowInfo(target_url
, guest_name
)));
1160 void WebViewGuest::LoadURLWithParams(const GURL
& url
,
1161 const content::Referrer
& referrer
,
1162 ui::PageTransition transition_type
,
1163 content::WebContents
* web_contents
) {
1164 content::NavigationController::LoadURLParams
load_url_params(url
);
1165 load_url_params
.referrer
= referrer
;
1166 load_url_params
.transition_type
= transition_type
;
1167 load_url_params
.extra_headers
= std::string();
1168 if (is_overriding_user_agent_
) {
1169 load_url_params
.override_user_agent
=
1170 content::NavigationController::UA_OVERRIDE_TRUE
;
1172 web_contents
->GetController().LoadURLWithParams(load_url_params
);
1175 void WebViewGuest::RequestNewWindowPermission(
1176 WindowOpenDisposition disposition
,
1177 const gfx::Rect
& initial_bounds
,
1179 content::WebContents
* new_contents
) {
1180 WebViewGuest
* guest
= WebViewGuest::FromWebContents(new_contents
);
1183 PendingWindowMap::iterator it
= pending_new_windows_
.find(guest
);
1184 if (it
== pending_new_windows_
.end())
1186 const NewWindowInfo
& new_window_info
= it
->second
;
1188 // Retrieve the opener partition info if we have it.
1189 const GURL
& site_url
= new_contents
->GetSiteInstance()->GetSiteURL();
1190 std::string storage_partition_id
= GetStoragePartitionIdFromSiteURL(site_url
);
1192 base::DictionaryValue request_info
;
1193 request_info
.SetInteger(webview::kInitialHeight
, initial_bounds
.height());
1194 request_info
.SetInteger(webview::kInitialWidth
, initial_bounds
.width());
1195 request_info
.Set(webview::kTargetURL
,
1196 new base::StringValue(new_window_info
.url
.spec()));
1197 request_info
.Set(webview::kName
, new base::StringValue(new_window_info
.name
));
1198 request_info
.SetInteger(webview::kWindowID
, guest
->guest_instance_id());
1199 // We pass in partition info so that window-s created through newwindow
1200 // API can use it to set their partition attribute.
1201 request_info
.Set(webview::kStoragePartitionId
,
1202 new base::StringValue(storage_partition_id
));
1204 webview::kWindowOpenDisposition
,
1205 new base::StringValue(WindowOpenDispositionToString(disposition
)));
1207 web_view_permission_helper_
->
1208 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
,
1210 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse
,
1211 weak_ptr_factory_
.GetWeakPtr(),
1212 guest
->guest_instance_id()),
1213 false /* allowed_by_default */);
1216 GURL
WebViewGuest::ResolveURL(const std::string
& src
) {
1217 if (!in_extension()) {
1221 GURL
default_url(base::StringPrintf("%s://%s/",
1223 embedder_extension_id().c_str()));
1224 return default_url
.Resolve(src
);
1227 void WebViewGuest::OnWebViewNewWindowResponse(
1228 int new_window_instance_id
,
1230 const std::string
& user_input
) {
1231 WebViewGuest
* guest
=
1232 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id
);
1240 } // namespace extensions