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/resource_request_details.h"
22 #include "content/public/browser/site_instance.h"
23 #include "content/public/browser/storage_partition.h"
24 #include "content/public/browser/user_metrics.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "content/public/common/media_stream_request.h"
28 #include "content/public/common/page_zoom.h"
29 #include "content/public/common/result_codes.h"
30 #include "content/public/common/stop_find_action.h"
31 #include "content/public/common/url_constants.h"
32 #include "extensions/browser/api/extensions_api_client.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/guest_view/guest_view_constants.h"
35 #include "extensions/browser/guest_view/guest_view_manager.h"
36 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
37 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
38 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
39 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
40 #include "extensions/common/constants.h"
41 #include "extensions/common/extension_messages.h"
42 #include "extensions/strings/grit/extensions_strings.h"
43 #include "ipc/ipc_message_macros.h"
44 #include "net/base/escape.h"
45 #include "net/base/net_errors.h"
46 #include "ui/base/models/simple_menu_model.h"
48 using base::UserMetricsAction
;
49 using content::RenderFrameHost
;
50 using content::ResourceType
;
51 using content::WebContents
;
53 namespace extensions
{
57 std::string
WindowOpenDispositionToString(
58 WindowOpenDisposition window_open_disposition
) {
59 switch (window_open_disposition
) {
63 return "save_to_disk";
66 case NEW_BACKGROUND_TAB
:
67 return "new_background_tab";
68 case NEW_FOREGROUND_TAB
:
69 return "new_foreground_tab";
75 NOTREACHED() << "Unknown Window Open Disposition";
80 static std::string
TerminationStatusToString(base::TerminationStatus status
) {
82 case base::TERMINATION_STATUS_NORMAL_TERMINATION
:
84 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
85 case base::TERMINATION_STATUS_STILL_RUNNING
:
87 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
89 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
91 case base::TERMINATION_STATUS_MAX_ENUM
:
94 NOTREACHED() << "Unknown Termination Status.";
98 std::string
GetStoragePartitionIdFromSiteURL(const GURL
& site_url
) {
99 const std::string
& partition_id
= site_url
.query();
100 bool persist_storage
= site_url
.path().find("persist") != std::string::npos
;
101 return (persist_storage
? webview::kPersistPrefix
: "") + partition_id
;
104 void ParsePartitionParam(const base::DictionaryValue
& create_params
,
105 std::string
* storage_partition_id
,
106 bool* persist_storage
) {
107 std::string partition_str
;
108 if (!create_params
.GetString(webview::kStoragePartitionId
, &partition_str
)) {
112 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
113 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
114 // remove the prefix without splicing in the middle of a multi-byte codepoint.
115 // We can use the rest of the string as UTF-8 encoded one.
116 if (StartsWithASCII(partition_str
, "persist:", true)) {
117 size_t index
= partition_str
.find(":");
118 CHECK(index
!= std::string::npos
);
119 // It is safe to do index + 1, since we tested for the full prefix above.
120 *storage_partition_id
= partition_str
.substr(index
+ 1);
122 if (storage_partition_id
->empty()) {
123 // TODO(lazyboy): Better way to deal with this error.
126 *persist_storage
= true;
128 *storage_partition_id
= partition_str
;
129 *persist_storage
= false;
136 GuestViewBase
* WebViewGuest::Create(content::BrowserContext
* browser_context
,
137 int guest_instance_id
) {
138 return new WebViewGuest(browser_context
, guest_instance_id
);
142 bool WebViewGuest::GetGuestPartitionConfigForSite(
144 std::string
* partition_domain
,
145 std::string
* partition_name
,
147 if (!site
.SchemeIs(content::kGuestScheme
))
150 // Since guest URLs are only used for packaged apps, there must be an app
152 CHECK(site
.has_host());
153 *partition_domain
= site
.host();
154 // Since persistence is optional, the path must either be empty or the
156 *in_memory
= (site
.path() != "/persist");
157 // The partition name is user supplied value, which we have encoded when the
158 // URL was created, so it needs to be decoded.
160 net::UnescapeURLComponent(site
.query(), net::UnescapeRule::NORMAL
);
165 const char WebViewGuest::Type
[] = "webview";
168 int WebViewGuest::GetViewInstanceId(WebContents
* contents
) {
169 WebViewGuest
* guest
= FromWebContents(contents
);
171 return guestview::kInstanceIDNone
;
173 return guest
->view_instance_id();
176 const char* WebViewGuest::GetAPINamespace() const {
177 return webview::kAPINamespace
;
180 int WebViewGuest::GetTaskPrefix() const {
181 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX
;
184 void WebViewGuest::CreateWebContents(
185 const std::string
& embedder_extension_id
,
186 int embedder_render_process_id
,
187 const base::DictionaryValue
& create_params
,
188 const WebContentsCreatedCallback
& callback
) {
189 content::RenderProcessHost
* embedder_render_process_host
=
190 content::RenderProcessHost::FromID(embedder_render_process_id
);
191 std::string storage_partition_id
;
192 bool persist_storage
= false;
193 std::string storage_partition_string
;
194 ParsePartitionParam(create_params
, &storage_partition_id
, &persist_storage
);
195 // Validate that the partition id coming from the renderer is valid UTF-8,
196 // since we depend on this in other parts of the code, such as FilePath
197 // creation. If the validation fails, treat it as a bad message and kill the
199 if (!base::IsStringUTF8(storage_partition_id
)) {
200 content::RecordAction(
201 base::UserMetricsAction("BadMessageTerminate_BPGM"));
203 embedder_render_process_host
->GetHandle(),
204 content::RESULT_CODE_KILLED_BAD_MESSAGE
, false);
208 std::string url_encoded_partition
= net::EscapeQueryParamValue(
209 storage_partition_id
, false);
210 // The SiteInstance of a given webview tag is based on the fact that it's
211 // a guest process in addition to which platform application the tag
212 // belongs to and what storage partition is in use, rather than the URL
213 // that the tag is being navigated to.
214 GURL
guest_site(base::StringPrintf("%s://%s/%s?%s",
215 content::kGuestScheme
,
216 embedder_extension_id
.c_str(),
217 persist_storage
? "persist" : "",
218 url_encoded_partition
.c_str()));
220 // If we already have a webview tag in the same app using the same storage
221 // partition, we should use the same SiteInstance so the existing tag and
222 // the new tag can script each other.
223 GuestViewManager
* guest_view_manager
=
224 GuestViewManager::FromBrowserContext(
225 embedder_render_process_host
->GetBrowserContext());
226 content::SiteInstance
* guest_site_instance
=
227 guest_view_manager
->GetGuestSiteInstance(guest_site
);
228 if (!guest_site_instance
) {
229 // Create the SiteInstance in a new BrowsingInstance, which will ensure
230 // that webview tags are also not allowed to send messages across
231 // different partitions.
232 guest_site_instance
= content::SiteInstance::CreateForURL(
233 embedder_render_process_host
->GetBrowserContext(), guest_site
);
235 WebContents::CreateParams
params(
236 embedder_render_process_host
->GetBrowserContext(),
237 guest_site_instance
);
238 params
.guest_delegate
= this;
239 callback
.Run(WebContents::Create(params
));
242 void WebViewGuest::DidAttachToEmbedder() {
246 if (attach_params()->GetString(webview::kName
, &name
)) {
247 // If the guest window's name is empty, then the WebView tag's name is
248 // assigned. Otherwise, the guest window's name takes precedence over the
249 // WebView tag's name.
253 ReportFrameNameChange(name_
);
255 std::string user_agent_override
;
256 if (attach_params()->GetString(webview::kParameterUserAgentOverride
,
257 &user_agent_override
)) {
258 SetUserAgentOverride(user_agent_override
);
260 SetUserAgentOverride("");
264 if (attach_params()->GetString("src", &src
) && !src
.empty())
268 // We need to do a navigation here if the target URL has changed between
269 // the time the WebContents was created and the time it was attached.
270 // We also need to do an initial navigation if a RenderView was never
271 // created for the new window in cases where there is no referrer.
272 PendingWindowMap::iterator it
=
273 GetOpener()->pending_new_windows_
.find(this);
274 if (it
!= GetOpener()->pending_new_windows_
.end()) {
275 const NewWindowInfo
& new_window_info
= it
->second
;
276 if (new_window_info
.changed
|| !guest_web_contents()->HasOpener())
277 NavigateGuest(new_window_info
.url
.spec());
282 // Once a new guest is attached to the DOM of the embedder page, then the
283 // lifetime of the new guest is no longer managed by the opener guest.
284 GetOpener()->pending_new_windows_
.erase(this);
288 void WebViewGuest::DidInitialize() {
289 script_executor_
.reset(
290 new ScriptExecutor(guest_web_contents(), &script_observers_
));
292 notification_registrar_
.Add(
293 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
294 content::Source
<WebContents
>(guest_web_contents()));
296 notification_registrar_
.Add(
297 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
298 content::Source
<WebContents
>(guest_web_contents()));
300 if (web_view_guest_delegate_
)
301 web_view_guest_delegate_
->OnDidInitialize();
302 AttachWebViewHelpers(guest_web_contents());
305 void WebViewGuest::AttachWebViewHelpers(WebContents
* contents
) {
306 if (web_view_guest_delegate_
)
307 web_view_guest_delegate_
->OnAttachWebViewHelpers(contents
);
308 web_view_permission_helper_
.reset(new WebViewPermissionHelper(this));
311 void WebViewGuest::DidStopLoading() {
312 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
313 DispatchEventToEmbedder(
314 new GuestViewBase::Event(webview::kEventLoadStop
, args
.Pass()));
317 void WebViewGuest::EmbedderDestroyed() {
318 if (web_view_guest_delegate_
)
319 web_view_guest_delegate_
->OnEmbedderDestroyed();
322 void WebViewGuest::GuestDestroyed() {
323 // Clean up custom context menu items for this guest.
324 if (web_view_guest_delegate_
)
325 web_view_guest_delegate_
->OnGuestDestroyed();
326 RemoveWebViewStateFromIOThread(web_contents());
329 void WebViewGuest::GuestReady() {
330 // The guest RenderView should always live in an isolated guest process.
331 CHECK(guest_web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
332 Send(new ExtensionMsg_SetFrameName(
333 guest_web_contents()->GetRoutingID(), name_
));
336 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size
& old_size
,
337 const gfx::Size
& new_size
) {
338 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
339 args
->SetInteger(webview::kOldHeight
, old_size
.height());
340 args
->SetInteger(webview::kOldWidth
, old_size
.width());
341 args
->SetInteger(webview::kNewHeight
, new_size
.height());
342 args
->SetInteger(webview::kNewWidth
, new_size
.width());
343 DispatchEventToEmbedder(
344 new GuestViewBase::Event(webview::kEventSizeChanged
, args
.Pass()));
347 bool WebViewGuest::IsAutoSizeSupported() const {
351 bool WebViewGuest::IsDragAndDropEnabled() const {
355 void WebViewGuest::WillDestroy() {
356 if (!attached() && GetOpener())
357 GetOpener()->pending_new_windows_
.erase(this);
358 DestroyUnattachedWindows();
360 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
361 DispatchEventToEmbedder(
362 new GuestViewBase::Event(webview::kEventPluginDestroyed
, args
.Pass()));
365 bool WebViewGuest::AddMessageToConsole(WebContents
* source
,
367 const base::string16
& message
,
369 const base::string16
& source_id
) {
370 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
371 // Log levels are from base/logging.h: LogSeverity.
372 args
->SetInteger(webview::kLevel
, level
);
373 args
->SetString(webview::kMessage
, message
);
374 args
->SetInteger(webview::kLine
, line_no
);
375 args
->SetString(webview::kSourceId
, source_id
);
376 DispatchEventToEmbedder(
377 new GuestViewBase::Event(webview::kEventConsoleMessage
, args
.Pass()));
381 void WebViewGuest::CloseContents(WebContents
* source
) {
382 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
383 DispatchEventToEmbedder(
384 new GuestViewBase::Event(webview::kEventClose
, args
.Pass()));
387 void WebViewGuest::FindReply(WebContents
* source
,
389 int number_of_matches
,
390 const gfx::Rect
& selection_rect
,
391 int active_match_ordinal
,
393 if (web_view_guest_delegate_
) {
394 web_view_guest_delegate_
->FindReply(
395 source
, request_id
, number_of_matches
,
396 selection_rect
, active_match_ordinal
, final_update
);
400 bool WebViewGuest::HandleContextMenu(
401 const content::ContextMenuParams
& params
) {
402 if (!web_view_guest_delegate_
)
404 return web_view_guest_delegate_
->HandleContextMenu(params
);
407 void WebViewGuest::HandleKeyboardEvent(
409 const content::NativeWebKeyboardEvent
& event
) {
413 if (HandleKeyboardShortcuts(event
))
416 // Send the unhandled keyboard events back to the embedder to reprocess them.
417 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
418 // events because the guest may be arbitrarily delayed when responding to
419 // keyboard events. In that time, the embedder may have received and processed
420 // additional key events. This needs to be fixed as soon as possible.
421 // See http://crbug.com/229882.
422 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
423 web_contents(), event
);
426 void WebViewGuest::LoadProgressChanged(content::WebContents
* source
,
428 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
429 args
->SetString(guestview::kUrl
, guest_web_contents()->GetURL().spec());
430 args
->SetDouble(webview::kProgress
, progress
);
431 DispatchEventToEmbedder(
432 new GuestViewBase::Event(webview::kEventLoadProgress
, args
.Pass()));
435 void WebViewGuest::LoadAbort(bool is_top_level
,
437 const std::string
& error_type
) {
438 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
439 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
440 args
->SetString(guestview::kUrl
, url
.possibly_invalid_spec());
441 args
->SetString(guestview::kReason
, error_type
);
442 DispatchEventToEmbedder(
443 new GuestViewBase::Event(webview::kEventLoadAbort
, args
.Pass()));
446 void WebViewGuest::OnFrameNameChanged(bool is_top_level
,
447 const std::string
& name
) {
454 ReportFrameNameChange(name
);
457 void WebViewGuest::CreateNewGuestWebViewWindow(
458 const content::OpenURLParams
& params
) {
459 GuestViewManager
* guest_manager
=
460 GuestViewManager::FromBrowserContext(browser_context());
461 // Set the attach params to use the same partition as the opener.
462 // We pull the partition information from the site's URL, which is of the
463 // form guest://site/{persist}?{partition_name}.
464 const GURL
& site_url
= guest_web_contents()->GetSiteInstance()->GetSiteURL();
465 const std::string storage_partition_id
=
466 GetStoragePartitionIdFromSiteURL(site_url
);
467 base::DictionaryValue create_params
;
468 create_params
.SetString(webview::kStoragePartitionId
, storage_partition_id
);
470 guest_manager
->CreateGuest(WebViewGuest::Type
,
471 embedder_extension_id(),
472 embedder_web_contents(),
474 base::Bind(&WebViewGuest::NewGuestWebViewCallback
,
475 base::Unretained(this),
479 void WebViewGuest::NewGuestWebViewCallback(
480 const content::OpenURLParams
& params
,
481 content::WebContents
* guest_web_contents
) {
482 WebViewGuest
* new_guest
= WebViewGuest::FromWebContents(guest_web_contents
);
483 new_guest
->SetOpener(this);
485 // Take ownership of |new_guest|.
486 pending_new_windows_
.insert(
487 std::make_pair(new_guest
, NewWindowInfo(params
.url
, std::string())));
489 // Request permission to show the new window.
490 RequestNewWindowPermission(params
.disposition
, gfx::Rect(),
492 new_guest
->guest_web_contents());
495 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
496 // 'unresponsive' events.
497 void WebViewGuest::RendererResponsive(content::WebContents
* source
) {
498 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
499 args
->SetInteger(webview::kProcessId
,
500 guest_web_contents()->GetRenderProcessHost()->GetID());
501 DispatchEventToEmbedder(
502 new GuestViewBase::Event(webview::kEventResponsive
, args
.Pass()));
505 void WebViewGuest::RendererUnresponsive(content::WebContents
* source
) {
506 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
507 args
->SetInteger(webview::kProcessId
,
508 guest_web_contents()->GetRenderProcessHost()->GetID());
509 DispatchEventToEmbedder(
510 new GuestViewBase::Event(webview::kEventUnresponsive
, args
.Pass()));
513 void WebViewGuest::Observe(int type
,
514 const content::NotificationSource
& source
,
515 const content::NotificationDetails
& details
) {
517 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
518 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
519 guest_web_contents());
520 if (content::Source
<WebContents
>(source
).ptr() == guest_web_contents())
524 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
525 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(),
526 guest_web_contents());
527 content::ResourceRedirectDetails
* resource_redirect_details
=
528 content::Details
<content::ResourceRedirectDetails
>(details
).ptr();
529 bool is_top_level
= resource_redirect_details
->resource_type
==
530 content::RESOURCE_TYPE_MAIN_FRAME
;
531 LoadRedirect(resource_redirect_details
->url
,
532 resource_redirect_details
->new_url
,
537 NOTREACHED() << "Unexpected notification sent.";
542 double WebViewGuest::GetZoom() {
543 if (!web_view_guest_delegate_
)
545 return web_view_guest_delegate_
->GetZoom();
548 void WebViewGuest::Find(
549 const base::string16
& search_text
,
550 const blink::WebFindOptions
& options
,
551 WebViewInternalFindFunction
* find_function
) {
552 if (web_view_guest_delegate_
)
553 web_view_guest_delegate_
->Find(search_text
, options
, find_function
);
556 void WebViewGuest::StopFinding(content::StopFindAction action
) {
557 if (web_view_guest_delegate_
)
558 web_view_guest_delegate_
->StopFinding(action
);
561 void WebViewGuest::Go(int relative_index
) {
562 guest_web_contents()->GetController().GoToOffset(relative_index
);
565 void WebViewGuest::Reload() {
566 // TODO(fsamuel): Don't check for repost because we don't want to show
567 // Chromium's repost warning. We might want to implement a separate API
568 // for registering a callback if a repost is about to happen.
569 guest_web_contents()->GetController().Reload(false);
572 void WebViewGuest::SetUserAgentOverride(
573 const std::string
& user_agent_override
) {
576 is_overriding_user_agent_
= !user_agent_override
.empty();
577 if (is_overriding_user_agent_
) {
578 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
580 guest_web_contents()->SetUserAgentOverride(user_agent_override
);
583 void WebViewGuest::Stop() {
584 guest_web_contents()->Stop();
587 void WebViewGuest::Terminate() {
588 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
589 base::ProcessHandle process_handle
=
590 guest_web_contents()->GetRenderProcessHost()->GetHandle();
592 base::KillProcess(process_handle
, content::RESULT_CODE_KILLED
, false);
595 bool WebViewGuest::ClearData(const base::Time remove_since
,
597 const base::Closure
& callback
) {
598 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
599 content::StoragePartition
* partition
=
600 content::BrowserContext::GetStoragePartition(
601 guest_web_contents()->GetBrowserContext(),
602 guest_web_contents()->GetSiteInstance());
607 partition
->ClearData(
609 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL
,
611 content::StoragePartition::OriginMatcherFunction(),
618 WebViewGuest::WebViewGuest(content::BrowserContext
* browser_context
,
619 int guest_instance_id
)
620 : GuestView
<WebViewGuest
>(browser_context
, guest_instance_id
),
621 is_overriding_user_agent_(false),
622 javascript_dialog_helper_(this) {
623 web_view_guest_delegate_
.reset(
624 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
627 WebViewGuest::~WebViewGuest() {
630 void WebViewGuest::DidCommitProvisionalLoadForFrame(
631 content::RenderFrameHost
* render_frame_host
,
633 content::PageTransition transition_type
) {
634 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
635 args
->SetString(guestview::kUrl
, url
.spec());
636 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
637 args
->SetInteger(webview::kInternalCurrentEntryIndex
,
638 guest_web_contents()->GetController().GetCurrentEntryIndex());
639 args
->SetInteger(webview::kInternalEntryCount
,
640 guest_web_contents()->GetController().GetEntryCount());
641 args
->SetInteger(webview::kInternalProcessId
,
642 guest_web_contents()->GetRenderProcessHost()->GetID());
643 DispatchEventToEmbedder(
644 new GuestViewBase::Event(webview::kEventLoadCommit
, args
.Pass()));
645 if (web_view_guest_delegate_
) {
646 web_view_guest_delegate_
->OnDidCommitProvisionalLoadForFrame(
647 !render_frame_host
->GetParent());
651 void WebViewGuest::DidFailProvisionalLoad(
652 content::RenderFrameHost
* render_frame_host
,
653 const GURL
& validated_url
,
655 const base::string16
& error_description
) {
656 LoadAbort(!render_frame_host
->GetParent(), validated_url
,
657 net::ErrorToShortString(error_code
));
660 void WebViewGuest::DidStartProvisionalLoadForFrame(
661 content::RenderFrameHost
* render_frame_host
,
662 const GURL
& validated_url
,
664 bool is_iframe_srcdoc
) {
665 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
666 args
->SetString(guestview::kUrl
, validated_url
.spec());
667 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
668 DispatchEventToEmbedder(
669 new GuestViewBase::Event(webview::kEventLoadStart
, args
.Pass()));
672 void WebViewGuest::DocumentLoadedInFrame(
673 content::RenderFrameHost
* render_frame_host
) {
674 if (web_view_guest_delegate_
)
675 web_view_guest_delegate_
->OnDocumentLoadedInFrame(render_frame_host
);
678 bool WebViewGuest::OnMessageReceived(const IPC::Message
& message
,
679 RenderFrameHost
* render_frame_host
) {
681 IPC_BEGIN_MESSAGE_MAP(WebViewGuest
, message
)
682 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged
, OnFrameNameChanged
)
683 IPC_MESSAGE_UNHANDLED(handled
= false)
684 IPC_END_MESSAGE_MAP()
688 void WebViewGuest::RenderProcessGone(base::TerminationStatus status
) {
689 if (web_view_guest_delegate_
)
690 web_view_guest_delegate_
->OnRenderProcessGone();
692 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
693 args
->SetInteger(webview::kProcessId
,
694 guest_web_contents()->GetRenderProcessHost()->GetID());
695 args
->SetString(webview::kReason
, TerminationStatusToString(status
));
696 DispatchEventToEmbedder(
697 new GuestViewBase::Event(webview::kEventExit
, args
.Pass()));
700 void WebViewGuest::UserAgentOverrideSet(const std::string
& user_agent
) {
703 content::NavigationController
& controller
=
704 guest_web_contents()->GetController();
705 content::NavigationEntry
* entry
= controller
.GetVisibleEntry();
708 entry
->SetIsOverridingUserAgent(!user_agent
.empty());
709 guest_web_contents()->GetController().Reload(false);
712 void WebViewGuest::ReportFrameNameChange(const std::string
& name
) {
714 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
715 args
->SetString(webview::kName
, name
);
716 DispatchEventToEmbedder(
717 new GuestViewBase::Event(webview::kEventFrameNameChanged
, args
.Pass()));
720 void WebViewGuest::LoadHandlerCalled() {
721 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
722 DispatchEventToEmbedder(
723 new GuestViewBase::Event(webview::kEventContentLoad
, args
.Pass()));
726 void WebViewGuest::LoadRedirect(const GURL
& old_url
,
729 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
730 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
731 args
->SetString(webview::kNewURL
, new_url
.spec());
732 args
->SetString(webview::kOldURL
, old_url
.spec());
733 DispatchEventToEmbedder(
734 new GuestViewBase::Event(webview::kEventLoadRedirect
, args
.Pass()));
737 void WebViewGuest::PushWebViewStateToIOThread() {
738 const GURL
& site_url
= guest_web_contents()->GetSiteInstance()->GetSiteURL();
739 std::string partition_domain
;
740 std::string partition_id
;
742 if (!GetGuestPartitionConfigForSite(
743 site_url
, &partition_domain
, &partition_id
, &in_memory
)) {
747 DCHECK(embedder_extension_id() == partition_domain
);
749 WebViewRendererState::WebViewInfo web_view_info
;
750 web_view_info
.embedder_process_id
= embedder_render_process_id();
751 web_view_info
.instance_id
= view_instance_id();
752 web_view_info
.partition_id
= partition_id
;
753 web_view_info
.embedder_extension_id
= embedder_extension_id();
755 content::BrowserThread::PostTask(
756 content::BrowserThread::IO
,
758 base::Bind(&WebViewRendererState::AddGuest
,
759 base::Unretained(WebViewRendererState::GetInstance()),
760 guest_web_contents()->GetRenderProcessHost()->GetID(),
761 guest_web_contents()->GetRoutingID(),
766 void WebViewGuest::RemoveWebViewStateFromIOThread(
767 WebContents
* web_contents
) {
768 content::BrowserThread::PostTask(
769 content::BrowserThread::IO
, FROM_HERE
,
771 &WebViewRendererState::RemoveGuest
,
772 base::Unretained(WebViewRendererState::GetInstance()),
773 web_contents
->GetRenderProcessHost()->GetID(),
774 web_contents
->GetRoutingID()));
777 content::WebContents
* WebViewGuest::CreateNewGuestWindow(
778 const content::WebContents::CreateParams
& create_params
) {
779 GuestViewManager
* guest_manager
=
780 GuestViewManager::FromBrowserContext(browser_context());
781 return guest_manager
->CreateGuestWithWebContentsParams(
783 embedder_extension_id(),
784 embedder_web_contents()->GetRenderProcessHost()->GetID(),
788 void WebViewGuest::RequestMediaAccessPermission(
789 content::WebContents
* source
,
790 const content::MediaStreamRequest
& request
,
791 const content::MediaResponseCallback
& callback
) {
792 web_view_permission_helper_
->RequestMediaAccessPermission(source
,
797 void WebViewGuest::CanDownload(
798 content::RenderViewHost
* render_view_host
,
800 const std::string
& request_method
,
801 const base::Callback
<void(bool)>& callback
) {
802 web_view_permission_helper_
->CanDownload(render_view_host
,
808 void WebViewGuest::RequestPointerLockPermission(
810 bool last_unlocked_by_target
,
811 const base::Callback
<void(bool)>& callback
) {
812 web_view_permission_helper_
->RequestPointerLockPermission(
814 last_unlocked_by_target
,
818 void WebViewGuest::WillAttachToEmbedder() {
819 // We must install the mapping from guests to WebViews prior to resuming
820 // suspended resource loads so that the WebRequest API will catch resource
822 PushWebViewStateToIOThread();
825 content::JavaScriptDialogManager
*
826 WebViewGuest::GetJavaScriptDialogManager() {
827 return &javascript_dialog_helper_
;
830 content::ColorChooser
* WebViewGuest::OpenColorChooser(
831 WebContents
* web_contents
,
833 const std::vector
<content::ColorSuggestion
>& suggestions
) {
834 if (!attached() || !embedder_web_contents()->GetDelegate())
836 return embedder_web_contents()->GetDelegate()->OpenColorChooser(
837 web_contents
, color
, suggestions
);
840 void WebViewGuest::RunFileChooser(WebContents
* web_contents
,
841 const content::FileChooserParams
& params
) {
842 if (!attached() || !embedder_web_contents()->GetDelegate())
845 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents
, params
);
848 void WebViewGuest::NavigateGuest(const std::string
& src
) {
849 GURL url
= ResolveURL(src
);
851 // Do not allow navigating a guest to schemes other than known safe schemes.
852 // This will block the embedder trying to load unwanted schemes, e.g.
853 // chrome://settings.
854 bool scheme_is_blocked
=
855 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
857 !url
.SchemeIs(url::kAboutScheme
)) ||
858 url
.SchemeIs(url::kJavaScriptScheme
);
859 if (scheme_is_blocked
|| !url
.is_valid()) {
860 LoadAbort(true /* is_top_level */, url
,
861 net::ErrorToShortString(net::ERR_ABORTED
));
865 GURL
validated_url(url
);
866 guest_web_contents()->GetRenderProcessHost()->
867 FilterURL(false, &validated_url
);
868 // As guests do not swap processes on navigation, only navigations to
869 // normal web URLs are supported. No protocol handlers are installed for
870 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
871 // can be granted to the guest process.
872 LoadURLWithParams(validated_url
,
874 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
875 guest_web_contents());
878 bool WebViewGuest::HandleKeyboardShortcuts(
879 const content::NativeWebKeyboardEvent
& event
) {
880 if (event
.type
!= blink::WebInputEvent::RawKeyDown
)
883 // If the user hits the escape key without any modifiers then unlock the
884 // mouse if necessary.
885 if ((event
.windowsKeyCode
== ui::VKEY_ESCAPE
) &&
886 !(event
.modifiers
& blink::WebInputEvent::InputModifiers
)) {
887 return guest_web_contents()->GotResponseToLockMouseRequest(false);
890 #if defined(OS_MACOSX)
891 if (event
.modifiers
!= blink::WebInputEvent::MetaKey
)
894 if (event
.windowsKeyCode
== ui::VKEY_OEM_4
) {
899 if (event
.windowsKeyCode
== ui::VKEY_OEM_6
) {
904 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_BACK
) {
909 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_FORWARD
) {
918 void WebViewGuest::SetUpAutoSize() {
919 // Read the autosize parameters passed in from the embedder.
920 bool auto_size_enabled
= false;
921 attach_params()->GetBoolean(webview::kAttributeAutoSize
, &auto_size_enabled
);
925 attach_params()->GetInteger(webview::kAttributeMaxHeight
, &max_height
);
926 attach_params()->GetInteger(webview::kAttributeMaxWidth
, &max_width
);
930 attach_params()->GetInteger(webview::kAttributeMinHeight
, &min_height
);
931 attach_params()->GetInteger(webview::kAttributeMinWidth
, &min_width
);
933 // Call SetAutoSize to apply all the appropriate validation and clipping of
935 SetAutoSize(auto_size_enabled
,
936 gfx::Size(min_width
, min_height
),
937 gfx::Size(max_width
, max_height
));
940 void WebViewGuest::ShowContextMenu(
942 const WebViewGuestDelegate::MenuItemVector
* items
) {
943 if (web_view_guest_delegate_
)
944 web_view_guest_delegate_
->OnShowContextMenu(request_id
, items
);
947 void WebViewGuest::SetName(const std::string
& name
) {
952 Send(new ExtensionMsg_SetFrameName(routing_id(), name_
));
955 void WebViewGuest::SetZoom(double zoom_factor
) {
956 if (web_view_guest_delegate_
)
957 web_view_guest_delegate_
->OnSetZoom(zoom_factor
);
960 void WebViewGuest::AddNewContents(content::WebContents
* source
,
961 content::WebContents
* new_contents
,
962 WindowOpenDisposition disposition
,
963 const gfx::Rect
& initial_pos
,
967 *was_blocked
= false;
968 RequestNewWindowPermission(disposition
,
974 content::WebContents
* WebViewGuest::OpenURLFromTab(
975 content::WebContents
* source
,
976 const content::OpenURLParams
& params
) {
977 // If the guest wishes to navigate away prior to attachment then we save the
978 // navigation to perform upon attachment. Navigation initializes a lot of
979 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
980 // Navigation also resumes resource loading which we don't want to allow
983 WebViewGuest
* opener
= GetOpener();
984 PendingWindowMap::iterator it
=
985 opener
->pending_new_windows_
.find(this);
986 if (it
== opener
->pending_new_windows_
.end())
988 const NewWindowInfo
& info
= it
->second
;
989 NewWindowInfo
new_window_info(params
.url
, info
.name
);
990 new_window_info
.changed
= new_window_info
.url
!= info
.url
;
991 it
->second
= new_window_info
;
994 if (params
.disposition
== CURRENT_TAB
) {
995 // This can happen for cross-site redirects.
996 LoadURLWithParams(params
.url
, params
.referrer
, params
.transition
, source
);
1000 CreateNewGuestWebViewWindow(params
);
1004 void WebViewGuest::WebContentsCreated(WebContents
* source_contents
,
1005 int opener_render_frame_id
,
1006 const base::string16
& frame_name
,
1007 const GURL
& target_url
,
1008 content::WebContents
* new_contents
) {
1009 WebViewGuest
* guest
= WebViewGuest::FromWebContents(new_contents
);
1011 guest
->SetOpener(this);
1012 std::string guest_name
= base::UTF16ToUTF8(frame_name
);
1013 guest
->name_
= guest_name
;
1014 pending_new_windows_
.insert(
1015 std::make_pair(guest
, NewWindowInfo(target_url
, guest_name
)));
1018 void WebViewGuest::LoadURLWithParams(const GURL
& url
,
1019 const content::Referrer
& referrer
,
1020 content::PageTransition transition_type
,
1021 content::WebContents
* web_contents
) {
1022 content::NavigationController::LoadURLParams
load_url_params(url
);
1023 load_url_params
.referrer
= referrer
;
1024 load_url_params
.transition_type
= transition_type
;
1025 load_url_params
.extra_headers
= std::string();
1026 if (is_overriding_user_agent_
) {
1027 load_url_params
.override_user_agent
=
1028 content::NavigationController::UA_OVERRIDE_TRUE
;
1030 web_contents
->GetController().LoadURLWithParams(load_url_params
);
1033 void WebViewGuest::RequestNewWindowPermission(
1034 WindowOpenDisposition disposition
,
1035 const gfx::Rect
& initial_bounds
,
1037 content::WebContents
* new_contents
) {
1038 WebViewGuest
* guest
= WebViewGuest::FromWebContents(new_contents
);
1041 PendingWindowMap::iterator it
= pending_new_windows_
.find(guest
);
1042 if (it
== pending_new_windows_
.end())
1044 const NewWindowInfo
& new_window_info
= it
->second
;
1046 // Retrieve the opener partition info if we have it.
1047 const GURL
& site_url
= new_contents
->GetSiteInstance()->GetSiteURL();
1048 std::string storage_partition_id
= GetStoragePartitionIdFromSiteURL(site_url
);
1050 base::DictionaryValue request_info
;
1051 request_info
.SetInteger(webview::kInitialHeight
, initial_bounds
.height());
1052 request_info
.SetInteger(webview::kInitialWidth
, initial_bounds
.width());
1053 request_info
.Set(webview::kTargetURL
,
1054 new base::StringValue(new_window_info
.url
.spec()));
1055 request_info
.Set(webview::kName
, new base::StringValue(new_window_info
.name
));
1056 request_info
.SetInteger(webview::kWindowID
, guest
->guest_instance_id());
1057 // We pass in partition info so that window-s created through newwindow
1058 // API can use it to set their partition attribute.
1059 request_info
.Set(webview::kStoragePartitionId
,
1060 new base::StringValue(storage_partition_id
));
1062 webview::kWindowOpenDisposition
,
1063 new base::StringValue(WindowOpenDispositionToString(disposition
)));
1065 web_view_permission_helper_
->
1066 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
,
1068 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse
,
1069 base::Unretained(this),
1070 guest
->guest_instance_id()),
1071 false /* allowed_by_default */);
1074 void WebViewGuest::DestroyUnattachedWindows() {
1075 // Destroy() reaches in and removes the WebViewGuest from its opener's
1076 // pending_new_windows_ set. To avoid mutating the set while iterating, we
1077 // create a copy of the pending new windows set and iterate over the copy.
1078 PendingWindowMap
pending_new_windows(pending_new_windows_
);
1079 // Clean up unattached new windows opened by this guest.
1080 for (PendingWindowMap::const_iterator it
= pending_new_windows
.begin();
1081 it
!= pending_new_windows
.end(); ++it
) {
1082 it
->first
->Destroy();
1084 // All pending windows should be removed from the set after Destroy() is
1085 // called on all of them.
1086 DCHECK(pending_new_windows_
.empty());
1089 GURL
WebViewGuest::ResolveURL(const std::string
& src
) {
1090 if (!in_extension()) {
1095 GURL
default_url(base::StringPrintf("%s://%s/",
1097 embedder_extension_id().c_str()));
1098 return default_url
.Resolve(src
);
1101 void WebViewGuest::OnWebViewNewWindowResponse(
1102 int new_window_instance_id
,
1104 const std::string
& user_input
) {
1105 WebViewGuest
* guest
=
1106 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id
);
1114 } // namespace extensions