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/declarative/rules_registry_service.h"
34 #include "extensions/browser/api/extensions_api_client.h"
35 #include "extensions/browser/api/web_request/web_request_api.h"
36 #include "extensions/browser/api/web_view/web_view_internal_api.h"
37 #include "extensions/browser/extension_system.h"
38 #include "extensions/browser/guest_view/guest_view_manager.h"
39 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
40 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
41 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
42 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
43 #include "extensions/common/constants.h"
44 #include "extensions/common/extension_messages.h"
45 #include "extensions/common/guest_view/guest_view_constants.h"
46 #include "extensions/strings/grit/extensions_strings.h"
47 #include "ipc/ipc_message_macros.h"
48 #include "net/base/escape.h"
49 #include "net/base/net_errors.h"
50 #include "ui/base/models/simple_menu_model.h"
51 #include "url/url_constants.h"
53 using base::UserMetricsAction
;
54 using content::RenderFrameHost
;
55 using content::ResourceType
;
56 using content::WebContents
;
58 namespace extensions
{
62 std::string
WindowOpenDispositionToString(
63 WindowOpenDisposition window_open_disposition
) {
64 switch (window_open_disposition
) {
68 return "save_to_disk";
71 case NEW_BACKGROUND_TAB
:
72 return "new_background_tab";
73 case NEW_FOREGROUND_TAB
:
74 return "new_foreground_tab";
80 NOTREACHED() << "Unknown Window Open Disposition";
85 static std::string
TerminationStatusToString(base::TerminationStatus status
) {
87 case base::TERMINATION_STATUS_NORMAL_TERMINATION
:
89 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
90 case base::TERMINATION_STATUS_STILL_RUNNING
:
92 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
94 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
96 case base::TERMINATION_STATUS_MAX_ENUM
:
99 NOTREACHED() << "Unknown Termination Status.";
103 std::string
GetStoragePartitionIdFromSiteURL(const GURL
& site_url
) {
104 const std::string
& partition_id
= site_url
.query();
105 bool persist_storage
= site_url
.path().find("persist") != std::string::npos
;
106 return (persist_storage
? webview::kPersistPrefix
: "") + partition_id
;
109 void ParsePartitionParam(const base::DictionaryValue
& create_params
,
110 std::string
* storage_partition_id
,
111 bool* persist_storage
) {
112 std::string partition_str
;
113 if (!create_params
.GetString(webview::kStoragePartitionId
, &partition_str
)) {
117 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
118 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
119 // remove the prefix without splicing in the middle of a multi-byte codepoint.
120 // We can use the rest of the string as UTF-8 encoded one.
121 if (StartsWithASCII(partition_str
, "persist:", true)) {
122 size_t index
= partition_str
.find(":");
123 CHECK(index
!= std::string::npos
);
124 // It is safe to do index + 1, since we tested for the full prefix above.
125 *storage_partition_id
= partition_str
.substr(index
+ 1);
127 if (storage_partition_id
->empty()) {
128 // TODO(lazyboy): Better way to deal with this error.
131 *persist_storage
= true;
133 *storage_partition_id
= partition_str
;
134 *persist_storage
= false;
138 void RemoveWebViewEventListenersOnIOThread(
140 const std::string
& extension_id
,
141 int embedder_process_id
,
142 int view_instance_id
) {
143 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
144 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
154 GuestViewBase
* WebViewGuest::Create(content::WebContents
* owner_web_contents
) {
155 return new WebViewGuest(owner_web_contents
);
159 bool WebViewGuest::GetGuestPartitionConfigForSite(
161 std::string
* partition_domain
,
162 std::string
* partition_name
,
164 if (!site
.SchemeIs(content::kGuestScheme
))
167 // Since guest URLs are only used for packaged apps, there must be an app
169 CHECK(site
.has_host());
170 *partition_domain
= site
.host();
171 // Since persistence is optional, the path must either be empty or the
173 *in_memory
= (site
.path() != "/persist");
174 // The partition name is user supplied value, which we have encoded when the
175 // URL was created, so it needs to be decoded.
177 net::UnescapeURLComponent(site
.query(), net::UnescapeRule::NORMAL
);
182 const char WebViewGuest::Type
[] = "webview";
184 using WebViewKey
= std::pair
<int, int>;
185 using WebViewKeyToIDMap
= std::map
<WebViewKey
, int>;
186 static base::LazyInstance
<WebViewKeyToIDMap
> web_view_key_to_id_map
=
187 LAZY_INSTANCE_INITIALIZER
;
190 int WebViewGuest::GetOrGenerateRulesRegistryID(
191 int embedder_process_id
,
192 int webview_instance_id
) {
193 bool is_web_view
= embedder_process_id
&& webview_instance_id
;
195 return RulesRegistryService::kDefaultRulesRegistryID
;
197 WebViewKey key
= std::make_pair(embedder_process_id
, webview_instance_id
);
198 auto it
= web_view_key_to_id_map
.Get().find(key
);
199 if (it
!= web_view_key_to_id_map
.Get().end())
202 auto rph
= content::RenderProcessHost::FromID(embedder_process_id
);
203 int rules_registry_id
=
204 RulesRegistryService::Get(rph
->GetBrowserContext())->
205 GetNextRulesRegistryID();
206 web_view_key_to_id_map
.Get()[key
] = rules_registry_id
;
207 return rules_registry_id
;
211 int WebViewGuest::GetViewInstanceId(WebContents
* contents
) {
212 auto guest
= FromWebContents(contents
);
214 return guestview::kInstanceIDNone
;
216 return guest
->view_instance_id();
219 bool WebViewGuest::CanRunInDetachedState() const {
223 void WebViewGuest::CreateWebContents(
224 const base::DictionaryValue
& create_params
,
225 const WebContentsCreatedCallback
& callback
) {
226 content::RenderProcessHost
* owner_render_process_host
=
227 owner_web_contents()->GetRenderProcessHost();
228 std::string storage_partition_id
;
229 bool persist_storage
= false;
230 ParsePartitionParam(create_params
, &storage_partition_id
, &persist_storage
);
231 // Validate that the partition id coming from the renderer is valid UTF-8,
232 // since we depend on this in other parts of the code, such as FilePath
233 // creation. If the validation fails, treat it as a bad message and kill the
235 if (!base::IsStringUTF8(storage_partition_id
)) {
236 content::RecordAction(
237 base::UserMetricsAction("BadMessageTerminate_BPGM"));
238 owner_render_process_host
->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
,
240 callback
.Run(nullptr);
243 std::string url_encoded_partition
= net::EscapeQueryParamValue(
244 storage_partition_id
, false);
245 std::string partition_domain
= GetOwnerSiteURL().host();
246 GURL
guest_site(base::StringPrintf("%s://%s/%s?%s",
247 content::kGuestScheme
,
248 partition_domain
.c_str(),
249 persist_storage
? "persist" : "",
250 url_encoded_partition
.c_str()));
252 // If we already have a webview tag in the same app using the same storage
253 // partition, we should use the same SiteInstance so the existing tag and
254 // the new tag can script each other.
255 auto guest_view_manager
= GuestViewManager::FromBrowserContext(
256 owner_render_process_host
->GetBrowserContext());
257 content::SiteInstance
* guest_site_instance
=
258 guest_view_manager
->GetGuestSiteInstance(guest_site
);
259 if (!guest_site_instance
) {
260 // Create the SiteInstance in a new BrowsingInstance, which will ensure
261 // that webview tags are also not allowed to send messages across
262 // different partitions.
263 guest_site_instance
= content::SiteInstance::CreateForURL(
264 owner_render_process_host
->GetBrowserContext(), guest_site
);
266 WebContents::CreateParams
params(
267 owner_render_process_host
->GetBrowserContext(),
268 guest_site_instance
);
269 params
.guest_delegate
= this;
270 callback
.Run(WebContents::Create(params
));
273 void WebViewGuest::DidAttachToEmbedder() {
274 ApplyAttributes(*attach_params());
277 void WebViewGuest::DidInitialize(const base::DictionaryValue
& create_params
) {
278 script_executor_
.reset(
279 new ScriptExecutor(web_contents(), &script_observers_
));
281 notification_registrar_
.Add(this,
282 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
283 content::Source
<WebContents
>(web_contents()));
285 notification_registrar_
.Add(this,
286 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
287 content::Source
<WebContents
>(web_contents()));
289 if (web_view_guest_delegate_
)
290 web_view_guest_delegate_
->OnDidInitialize();
291 AttachWebViewHelpers(web_contents());
293 rules_registry_id_
= GetOrGenerateRulesRegistryID(
294 owner_web_contents()->GetRenderProcessHost()->GetID(),
297 // We must install the mapping from guests to WebViews prior to resuming
298 // suspended resource loads so that the WebRequest API will catch resource
300 PushWebViewStateToIOThread();
302 ApplyAttributes(create_params
);
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());
314 new GuestViewBase::Event(webview::kEventLoadStop
, args
.Pass()));
317 void WebViewGuest::EmbedderWillBeDestroyed() {
318 // Clean up rules registries for the webview.
319 RulesRegistryService::Get(browser_context())
320 ->RemoveRulesRegistriesByID(rules_registry_id_
);
321 WebViewKey
key(owner_web_contents()->GetRenderProcessHost()->GetID(),
323 web_view_key_to_id_map
.Get().erase(key
);
325 content::BrowserThread::PostTask(
326 content::BrowserThread::IO
,
329 &RemoveWebViewEventListenersOnIOThread
,
331 owner_extension_id(),
332 owner_web_contents()->GetRenderProcessHost()->GetID(),
333 view_instance_id()));
336 const char* WebViewGuest::GetAPINamespace() const {
337 return webview::kAPINamespace
;
340 int WebViewGuest::GetTaskPrefix() const {
341 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX
;
344 void WebViewGuest::GuestDestroyed() {
345 // Clean up custom context menu items for this guest.
346 if (web_view_guest_delegate_
)
347 web_view_guest_delegate_
->OnGuestDestroyed();
348 RemoveWebViewStateFromIOThread(web_contents());
351 void WebViewGuest::GuestReady() {
352 // The guest RenderView should always live in an isolated guest process.
353 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
354 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_
));
356 // We don't want to accidentally set the opacity of an interstitial page.
357 // WebContents::GetRenderWidgetHostView will return the RWHV of an
358 // interstitial page if one is showing at this time. We only want opacity
359 // to apply to web pages.
362 ->GetRenderViewHost()
364 ->SetBackgroundColorToDefault();
366 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
367 SK_ColorTRANSPARENT
);
371 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size
& old_size
,
372 const gfx::Size
& new_size
) {
373 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
374 args
->SetInteger(webview::kOldHeight
, old_size
.height());
375 args
->SetInteger(webview::kOldWidth
, old_size
.width());
376 args
->SetInteger(webview::kNewHeight
, new_size
.height());
377 args
->SetInteger(webview::kNewWidth
, new_size
.width());
379 new GuestViewBase::Event(webview::kEventSizeChanged
, args
.Pass()));
382 bool WebViewGuest::IsAutoSizeSupported() const {
386 bool WebViewGuest::IsDragAndDropEnabled() const {
390 void WebViewGuest::WillDestroy() {
391 if (!attached() && GetOpener())
392 GetOpener()->pending_new_windows_
.erase(this);
395 bool WebViewGuest::AddMessageToConsole(WebContents
* source
,
397 const base::string16
& message
,
399 const base::string16
& source_id
) {
400 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
401 // Log levels are from base/logging.h: LogSeverity.
402 args
->SetInteger(webview::kLevel
, level
);
403 args
->SetString(webview::kMessage
, message
);
404 args
->SetInteger(webview::kLine
, line_no
);
405 args
->SetString(webview::kSourceId
, source_id
);
407 new GuestViewBase::Event(webview::kEventConsoleMessage
, args
.Pass()));
411 void WebViewGuest::CloseContents(WebContents
* source
) {
412 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
414 new GuestViewBase::Event(webview::kEventClose
, args
.Pass()));
417 void WebViewGuest::FindReply(WebContents
* source
,
419 int number_of_matches
,
420 const gfx::Rect
& selection_rect
,
421 int active_match_ordinal
,
423 find_helper_
.FindReply(request_id
,
426 active_match_ordinal
,
430 bool WebViewGuest::HandleContextMenu(
431 const content::ContextMenuParams
& params
) {
432 if (!web_view_guest_delegate_
)
434 return web_view_guest_delegate_
->HandleContextMenu(params
);
437 void WebViewGuest::HandleKeyboardEvent(
439 const content::NativeWebKeyboardEvent
& event
) {
440 if (HandleKeyboardShortcuts(event
))
443 GuestViewBase::HandleKeyboardEvent(source
, event
);
446 void WebViewGuest::LoadProgressChanged(content::WebContents
* source
,
448 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
449 args
->SetString(guestview::kUrl
, web_contents()->GetURL().spec());
450 args
->SetDouble(webview::kProgress
, progress
);
452 new GuestViewBase::Event(webview::kEventLoadProgress
, args
.Pass()));
455 void WebViewGuest::LoadAbort(bool is_top_level
,
457 const std::string
& error_type
) {
458 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
459 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
460 args
->SetString(guestview::kUrl
, url
.possibly_invalid_spec());
461 args
->SetString(guestview::kReason
, error_type
);
463 new GuestViewBase::Event(webview::kEventLoadAbort
, args
.Pass()));
466 void WebViewGuest::OnFrameNameChanged(bool is_top_level
,
467 const std::string
& name
) {
474 ReportFrameNameChange(name
);
477 void WebViewGuest::CreateNewGuestWebViewWindow(
478 const content::OpenURLParams
& params
) {
479 GuestViewManager
* guest_manager
=
480 GuestViewManager::FromBrowserContext(browser_context());
481 // Set the attach params to use the same partition as the opener.
482 // We pull the partition information from the site's URL, which is of the
483 // form guest://site/{persist}?{partition_name}.
484 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
485 const std::string storage_partition_id
=
486 GetStoragePartitionIdFromSiteURL(site_url
);
487 base::DictionaryValue create_params
;
488 create_params
.SetString(webview::kStoragePartitionId
, storage_partition_id
);
490 guest_manager
->CreateGuest(WebViewGuest::Type
,
491 embedder_web_contents(),
493 base::Bind(&WebViewGuest::NewGuestWebViewCallback
,
494 weak_ptr_factory_
.GetWeakPtr(),
498 void WebViewGuest::NewGuestWebViewCallback(
499 const content::OpenURLParams
& params
,
500 content::WebContents
* guest_web_contents
) {
501 WebViewGuest
* new_guest
= WebViewGuest::FromWebContents(guest_web_contents
);
502 new_guest
->SetOpener(this);
504 // Take ownership of |new_guest|.
505 pending_new_windows_
.insert(
506 std::make_pair(new_guest
, NewWindowInfo(params
.url
, std::string())));
508 // Request permission to show the new window.
509 RequestNewWindowPermission(params
.disposition
,
512 new_guest
->web_contents());
515 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
516 // 'unresponsive' events.
517 void WebViewGuest::RendererResponsive(content::WebContents
* source
) {
518 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
519 args
->SetInteger(webview::kProcessId
,
520 web_contents()->GetRenderProcessHost()->GetID());
522 new GuestViewBase::Event(webview::kEventResponsive
, args
.Pass()));
525 void WebViewGuest::RendererUnresponsive(content::WebContents
* source
) {
526 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
527 args
->SetInteger(webview::kProcessId
,
528 web_contents()->GetRenderProcessHost()->GetID());
530 new GuestViewBase::Event(webview::kEventUnresponsive
, args
.Pass()));
533 void WebViewGuest::Observe(int type
,
534 const content::NotificationSource
& source
,
535 const content::NotificationDetails
& details
) {
537 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
538 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
539 if (content::Source
<WebContents
>(source
).ptr() == web_contents())
543 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
544 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
545 content::ResourceRedirectDetails
* resource_redirect_details
=
546 content::Details
<content::ResourceRedirectDetails
>(details
).ptr();
547 bool is_top_level
= resource_redirect_details
->resource_type
==
548 content::RESOURCE_TYPE_MAIN_FRAME
;
549 LoadRedirect(resource_redirect_details
->url
,
550 resource_redirect_details
->new_url
,
555 NOTREACHED() << "Unexpected notification sent.";
560 void WebViewGuest::StartFindInternal(
561 const base::string16
& search_text
,
562 const blink::WebFindOptions
& options
,
563 scoped_refptr
<WebViewInternalFindFunction
> find_function
) {
564 find_helper_
.Find(web_contents(), search_text
, options
, find_function
);
567 void WebViewGuest::StopFindingInternal(content::StopFindAction action
) {
568 find_helper_
.CancelAllFindSessions();
569 web_contents()->StopFinding(action
);
572 bool WebViewGuest::Go(int relative_index
) {
573 content::NavigationController
& controller
= web_contents()->GetController();
574 if (!controller
.CanGoToOffset(relative_index
))
577 controller
.GoToOffset(relative_index
);
581 void WebViewGuest::Reload() {
582 // TODO(fsamuel): Don't check for repost because we don't want to show
583 // Chromium's repost warning. We might want to implement a separate API
584 // for registering a callback if a repost is about to happen.
585 web_contents()->GetController().Reload(false);
588 void WebViewGuest::SetUserAgentOverride(
589 const std::string
& user_agent_override
) {
590 is_overriding_user_agent_
= !user_agent_override
.empty();
591 if (is_overriding_user_agent_
) {
592 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
594 web_contents()->SetUserAgentOverride(user_agent_override
);
597 void WebViewGuest::Stop() {
598 web_contents()->Stop();
601 void WebViewGuest::Terminate() {
602 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
603 base::ProcessHandle process_handle
=
604 web_contents()->GetRenderProcessHost()->GetHandle();
606 web_contents()->GetRenderProcessHost()->Shutdown(
607 content::RESULT_CODE_KILLED
, false);
610 bool WebViewGuest::ClearData(const base::Time remove_since
,
612 const base::Closure
& callback
) {
613 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
614 content::StoragePartition
* partition
=
615 content::BrowserContext::GetStoragePartition(
616 web_contents()->GetBrowserContext(),
617 web_contents()->GetSiteInstance());
622 partition
->ClearData(
624 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL
,
626 content::StoragePartition::OriginMatcherFunction(),
633 WebViewGuest::WebViewGuest(content::WebContents
* owner_web_contents
)
634 : GuestView
<WebViewGuest
>(owner_web_contents
),
635 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID
),
637 is_overriding_user_agent_(false),
639 javascript_dialog_helper_(this),
640 current_zoom_factor_(1.0),
641 weak_ptr_factory_(this) {
642 web_view_guest_delegate_
.reset(
643 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
646 WebViewGuest::~WebViewGuest() {
649 void WebViewGuest::DidCommitProvisionalLoadForFrame(
650 content::RenderFrameHost
* render_frame_host
,
652 ui::PageTransition transition_type
) {
653 if (!render_frame_host
->GetParent())
655 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
656 args
->SetString(guestview::kUrl
, url
.spec());
657 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
658 args
->SetString(webview::kInternalBaseURLForDataURL
,
661 .GetLastCommittedEntry()
662 ->GetBaseURLForDataURL()
664 args
->SetInteger(webview::kInternalCurrentEntryIndex
,
665 web_contents()->GetController().GetCurrentEntryIndex());
666 args
->SetInteger(webview::kInternalEntryCount
,
667 web_contents()->GetController().GetEntryCount());
668 args
->SetInteger(webview::kInternalProcessId
,
669 web_contents()->GetRenderProcessHost()->GetID());
671 new GuestViewBase::Event(webview::kEventLoadCommit
, args
.Pass()));
673 find_helper_
.CancelAllFindSessions();
675 // Update the current zoom factor for the new page.
676 ui_zoom::ZoomController
* zoom_controller
=
677 ui_zoom::ZoomController::FromWebContents(web_contents());
678 DCHECK(zoom_controller
);
679 current_zoom_factor_
=
680 content::ZoomLevelToZoomFactor(zoom_controller
->GetZoomLevel());
682 if (web_view_guest_delegate_
) {
683 web_view_guest_delegate_
->OnDidCommitProvisionalLoadForFrame(
684 !render_frame_host
->GetParent());
688 void WebViewGuest::DidFailProvisionalLoad(
689 content::RenderFrameHost
* render_frame_host
,
690 const GURL
& validated_url
,
692 const base::string16
& error_description
) {
693 LoadAbort(!render_frame_host
->GetParent(), validated_url
,
694 net::ErrorToShortString(error_code
));
697 void WebViewGuest::DidStartProvisionalLoadForFrame(
698 content::RenderFrameHost
* render_frame_host
,
699 const GURL
& validated_url
,
701 bool is_iframe_srcdoc
) {
702 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
703 args
->SetString(guestview::kUrl
, validated_url
.spec());
704 args
->SetBoolean(guestview::kIsTopLevel
, !render_frame_host
->GetParent());
706 new GuestViewBase::Event(webview::kEventLoadStart
, args
.Pass()));
709 void WebViewGuest::DocumentLoadedInFrame(
710 content::RenderFrameHost
* render_frame_host
) {
711 if (web_view_guest_delegate_
)
712 web_view_guest_delegate_
->OnDocumentLoadedInFrame(render_frame_host
);
715 bool WebViewGuest::OnMessageReceived(const IPC::Message
& message
,
716 RenderFrameHost
* render_frame_host
) {
718 IPC_BEGIN_MESSAGE_MAP(WebViewGuest
, message
)
719 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged
, OnFrameNameChanged
)
720 IPC_MESSAGE_UNHANDLED(handled
= false)
721 IPC_END_MESSAGE_MAP()
725 void WebViewGuest::RenderProcessGone(base::TerminationStatus status
) {
726 // Cancel all find sessions in progress.
727 find_helper_
.CancelAllFindSessions();
729 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
730 args
->SetInteger(webview::kProcessId
,
731 web_contents()->GetRenderProcessHost()->GetID());
732 args
->SetString(webview::kReason
, TerminationStatusToString(status
));
734 new GuestViewBase::Event(webview::kEventExit
, args
.Pass()));
737 void WebViewGuest::UserAgentOverrideSet(const std::string
& user_agent
) {
738 content::NavigationController
& controller
= web_contents()->GetController();
739 content::NavigationEntry
* entry
= controller
.GetVisibleEntry();
742 entry
->SetIsOverridingUserAgent(!user_agent
.empty());
743 web_contents()->GetController().Reload(false);
746 void WebViewGuest::ReportFrameNameChange(const std::string
& name
) {
748 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
749 args
->SetString(webview::kName
, name
);
751 new GuestViewBase::Event(webview::kEventFrameNameChanged
, args
.Pass()));
754 void WebViewGuest::LoadHandlerCalled() {
755 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
757 new GuestViewBase::Event(webview::kEventContentLoad
, args
.Pass()));
760 void WebViewGuest::LoadRedirect(const GURL
& old_url
,
763 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
764 args
->SetBoolean(guestview::kIsTopLevel
, is_top_level
);
765 args
->SetString(webview::kNewURL
, new_url
.spec());
766 args
->SetString(webview::kOldURL
, old_url
.spec());
768 new GuestViewBase::Event(webview::kEventLoadRedirect
, args
.Pass()));
771 void WebViewGuest::PushWebViewStateToIOThread() {
772 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
773 std::string partition_domain
;
774 std::string partition_id
;
776 if (!GetGuestPartitionConfigForSite(
777 site_url
, &partition_domain
, &partition_id
, &in_memory
)) {
782 WebViewRendererState::WebViewInfo web_view_info
;
783 web_view_info
.embedder_process_id
=
784 owner_web_contents()->GetRenderProcessHost()->GetID();
785 web_view_info
.instance_id
= view_instance_id();
786 web_view_info
.partition_id
= partition_id
;
787 web_view_info
.owner_extension_id
= owner_extension_id();
788 web_view_info
.rules_registry_id
= rules_registry_id_
;
790 content::BrowserThread::PostTask(
791 content::BrowserThread::IO
,
793 base::Bind(&WebViewRendererState::AddGuest
,
794 base::Unretained(WebViewRendererState::GetInstance()),
795 web_contents()->GetRenderProcessHost()->GetID(),
796 web_contents()->GetRoutingID(),
801 void WebViewGuest::RemoveWebViewStateFromIOThread(
802 WebContents
* web_contents
) {
803 content::BrowserThread::PostTask(
804 content::BrowserThread::IO
, FROM_HERE
,
806 &WebViewRendererState::RemoveGuest
,
807 base::Unretained(WebViewRendererState::GetInstance()),
808 web_contents
->GetRenderProcessHost()->GetID(),
809 web_contents
->GetRoutingID()));
812 void WebViewGuest::RequestMediaAccessPermission(
813 content::WebContents
* source
,
814 const content::MediaStreamRequest
& request
,
815 const content::MediaResponseCallback
& callback
) {
816 web_view_permission_helper_
->RequestMediaAccessPermission(source
,
821 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents
* source
,
822 const GURL
& security_origin
,
823 content::MediaStreamType type
) {
824 return web_view_permission_helper_
->CheckMediaAccessPermission(
825 source
, security_origin
, type
);
828 void WebViewGuest::CanDownload(
829 content::RenderViewHost
* render_view_host
,
831 const std::string
& request_method
,
832 const base::Callback
<void(bool)>& callback
) {
833 web_view_permission_helper_
->CanDownload(render_view_host
,
839 void WebViewGuest::RequestPointerLockPermission(
841 bool last_unlocked_by_target
,
842 const base::Callback
<void(bool)>& callback
) {
843 web_view_permission_helper_
->RequestPointerLockPermission(
845 last_unlocked_by_target
,
849 void WebViewGuest::WillAttachToEmbedder() {
850 rules_registry_id_
= GetOrGenerateRulesRegistryID(
851 owner_web_contents()->GetRenderProcessHost()->GetID(),
854 // We must install the mapping from guests to WebViews prior to resuming
855 // suspended resource loads so that the WebRequest API will catch resource
857 PushWebViewStateToIOThread();
860 content::JavaScriptDialogManager
* WebViewGuest::GetJavaScriptDialogManager(
861 WebContents
* source
) {
862 return &javascript_dialog_helper_
;
865 void WebViewGuest::NavigateGuest(const std::string
& src
,
866 bool force_navigation
) {
870 GURL url
= ResolveURL(src
);
872 // Do not allow navigating a guest to schemes other than known safe schemes.
873 // This will block the embedder trying to load unwanted schemes, e.g.
874 // chrome://settings.
875 bool scheme_is_blocked
=
876 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
878 !url
.SchemeIs(url::kAboutScheme
)) ||
879 url
.SchemeIs(url::kJavaScriptScheme
);
880 if (scheme_is_blocked
|| !url
.is_valid()) {
881 LoadAbort(true /* is_top_level */, url
,
882 net::ErrorToShortString(net::ERR_ABORTED
));
883 NavigateGuest(url::kAboutBlankURL
, true /* force_navigation */);
886 if (!force_navigation
&& (src_
== url
))
889 GURL
validated_url(url
);
890 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url
);
891 // As guests do not swap processes on navigation, only navigations to
892 // normal web URLs are supported. No protocol handlers are installed for
893 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
894 // can be granted to the guest process.
895 LoadURLWithParams(validated_url
,
897 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
900 src_
= validated_url
;
903 bool WebViewGuest::HandleKeyboardShortcuts(
904 const content::NativeWebKeyboardEvent
& event
) {
905 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
909 if (event
.type
!= blink::WebInputEvent::RawKeyDown
)
912 // If the user hits the escape key without any modifiers then unlock the
913 // mouse if necessary.
914 if ((event
.windowsKeyCode
== ui::VKEY_ESCAPE
) &&
915 !(event
.modifiers
& blink::WebInputEvent::InputModifiers
)) {
916 return web_contents()->GotResponseToLockMouseRequest(false);
919 #if defined(OS_MACOSX)
920 if (event
.modifiers
!= blink::WebInputEvent::MetaKey
)
923 if (event
.windowsKeyCode
== ui::VKEY_OEM_4
) {
928 if (event
.windowsKeyCode
== ui::VKEY_OEM_6
) {
933 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_BACK
) {
938 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_FORWARD
) {
947 void WebViewGuest::ApplyAttributes(const base::DictionaryValue
& params
) {
949 if (params
.GetString(webview::kAttributeName
, &name
)) {
950 // If the guest window's name is empty, then the WebView tag's name is
951 // assigned. Otherwise, the guest window's name takes precedence over the
952 // WebView tag's name.
957 ReportFrameNameChange(name_
);
959 std::string user_agent_override
;
960 params
.GetString(webview::kParameterUserAgentOverride
, &user_agent_override
);
961 SetUserAgentOverride(user_agent_override
);
963 bool allow_transparency
= false;
964 params
.GetBoolean(webview::kAttributeAllowTransparency
, &allow_transparency
);
965 // We need to set the background opaque flag after navigation to ensure that
966 // there is a RenderWidgetHostView available.
967 SetAllowTransparency(allow_transparency
);
969 bool is_pending_new_window
= false;
971 // We need to do a navigation here if the target URL has changed between
972 // the time the WebContents was created and the time it was attached.
973 // We also need to do an initial navigation if a RenderView was never
974 // created for the new window in cases where there is no referrer.
975 auto it
= GetOpener()->pending_new_windows_
.find(this);
976 if (it
!= GetOpener()->pending_new_windows_
.end()) {
977 const NewWindowInfo
& new_window_info
= it
->second
;
978 if (new_window_info
.changed
|| !web_contents()->HasOpener())
979 NavigateGuest(new_window_info
.url
.spec(), false /* force_navigation */);
981 // Once a new guest is attached to the DOM of the embedder page, then the
982 // lifetime of the new guest is no longer managed by the opener guest.
983 GetOpener()->pending_new_windows_
.erase(this);
985 is_pending_new_window
= true;
989 // Only read the src attribute if this is not a New Window API flow.
990 if (!is_pending_new_window
) {
992 params
.GetString(webview::kAttributeSrc
, &src
);
993 NavigateGuest(src
, false /* force_navigation */);
998 void WebViewGuest::ShowContextMenu(
1000 const WebViewGuestDelegate::MenuItemVector
* items
) {
1001 if (web_view_guest_delegate_
)
1002 web_view_guest_delegate_
->OnShowContextMenu(request_id
, items
);
1005 void WebViewGuest::SetName(const std::string
& name
) {
1010 Send(new ExtensionMsg_SetFrameName(routing_id(), name_
));
1013 void WebViewGuest::SetZoom(double zoom_factor
) {
1014 auto zoom_controller
=
1015 ui_zoom::ZoomController::FromWebContents(web_contents());
1016 DCHECK(zoom_controller
);
1017 double zoom_level
= content::ZoomFactorToZoomLevel(zoom_factor
);
1018 zoom_controller
->SetZoomLevel(zoom_level
);
1020 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
1021 args
->SetDouble(webview::kOldZoomFactor
, current_zoom_factor_
);
1022 args
->SetDouble(webview::kNewZoomFactor
, zoom_factor
);
1023 DispatchEventToView(
1024 new GuestViewBase::Event(webview::kEventZoomChange
, args
.Pass()));
1025 current_zoom_factor_
= zoom_factor
;
1028 void WebViewGuest::SetAllowTransparency(bool allow
) {
1029 if (guest_opaque_
!= allow
)
1032 guest_opaque_
= !allow
;
1033 if (!web_contents()->GetRenderViewHost()->GetView())
1036 if (guest_opaque_
) {
1038 ->GetRenderViewHost()
1040 ->SetBackgroundColorToDefault();
1042 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1043 SK_ColorTRANSPARENT
);
1047 bool WebViewGuest::LoadDataWithBaseURL(const std::string
& data_url
,
1048 const std::string
& base_url
,
1049 const std::string
& virtual_url
,
1050 std::string
* error
) {
1051 // Make GURLs from URLs.
1052 const GURL data_gurl
= GURL(data_url
);
1053 const GURL base_gurl
= GURL(base_url
);
1054 const GURL virtual_gurl
= GURL(virtual_url
);
1056 // Check that the provided URLs are valid.
1057 // |data_url| must be a valid data URL.
1058 if (!data_gurl
.is_valid() || !data_gurl
.SchemeIs(url::kDataScheme
)) {
1059 base::SStringPrintf(
1060 error
, webview::kAPILoadDataInvalidDataURL
, data_url
.c_str());
1063 // |base_url| must be a valid URL.
1064 if (!base_gurl
.is_valid()) {
1065 base::SStringPrintf(
1066 error
, webview::kAPILoadDataInvalidBaseURL
, base_url
.c_str());
1069 // |virtual_url| must be a valid URL.
1070 if (!virtual_gurl
.is_valid()) {
1071 base::SStringPrintf(
1072 error
, webview::kAPILoadDataInvalidVirtualURL
, virtual_url
.c_str());
1076 // Set up the parameters to load |data_url| with the specified |base_url|.
1077 content::NavigationController::LoadURLParams
load_params(data_gurl
);
1078 load_params
.load_type
= content::NavigationController::LOAD_TYPE_DATA
;
1079 load_params
.base_url_for_data_url
= base_gurl
;
1080 load_params
.virtual_url_for_data_url
= virtual_gurl
;
1081 load_params
.override_user_agent
=
1082 content::NavigationController::UA_OVERRIDE_INHERIT
;
1084 // Navigate to the data URL.
1085 web_contents()->GetController().LoadURLWithParams(load_params
);
1090 void WebViewGuest::AddNewContents(content::WebContents
* source
,
1091 content::WebContents
* new_contents
,
1092 WindowOpenDisposition disposition
,
1093 const gfx::Rect
& initial_rect
,
1095 bool* was_blocked
) {
1097 *was_blocked
= false;
1098 RequestNewWindowPermission(disposition
,
1104 content::WebContents
* WebViewGuest::OpenURLFromTab(
1105 content::WebContents
* source
,
1106 const content::OpenURLParams
& params
) {
1107 // If the guest wishes to navigate away prior to attachment then we save the
1108 // navigation to perform upon attachment. Navigation initializes a lot of
1109 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1110 // Navigation also resumes resource loading which we don't want to allow
1111 // until attachment.
1113 WebViewGuest
* opener
= GetOpener();
1114 auto it
= opener
->pending_new_windows_
.find(this);
1115 if (it
== opener
->pending_new_windows_
.end())
1117 const NewWindowInfo
& info
= it
->second
;
1118 NewWindowInfo
new_window_info(params
.url
, info
.name
);
1119 new_window_info
.changed
= new_window_info
.url
!= info
.url
;
1120 it
->second
= new_window_info
;
1123 if (params
.disposition
== CURRENT_TAB
) {
1124 // This can happen for cross-site redirects.
1125 LoadURLWithParams(params
.url
, params
.referrer
, params
.transition
, source
);
1129 CreateNewGuestWebViewWindow(params
);
1133 void WebViewGuest::WebContentsCreated(WebContents
* source_contents
,
1134 int opener_render_frame_id
,
1135 const base::string16
& frame_name
,
1136 const GURL
& target_url
,
1137 content::WebContents
* new_contents
) {
1138 auto guest
= WebViewGuest::FromWebContents(new_contents
);
1140 guest
->SetOpener(this);
1141 std::string guest_name
= base::UTF16ToUTF8(frame_name
);
1142 guest
->name_
= guest_name
;
1143 pending_new_windows_
.insert(
1144 std::make_pair(guest
, NewWindowInfo(target_url
, guest_name
)));
1147 void WebViewGuest::LoadURLWithParams(const GURL
& url
,
1148 const content::Referrer
& referrer
,
1149 ui::PageTransition transition_type
,
1150 content::WebContents
* web_contents
) {
1151 content::NavigationController::LoadURLParams
load_url_params(url
);
1152 load_url_params
.referrer
= referrer
;
1153 load_url_params
.transition_type
= transition_type
;
1154 load_url_params
.extra_headers
= std::string();
1155 if (is_overriding_user_agent_
) {
1156 load_url_params
.override_user_agent
=
1157 content::NavigationController::UA_OVERRIDE_TRUE
;
1159 web_contents
->GetController().LoadURLWithParams(load_url_params
);
1162 void WebViewGuest::RequestNewWindowPermission(
1163 WindowOpenDisposition disposition
,
1164 const gfx::Rect
& initial_bounds
,
1166 content::WebContents
* new_contents
) {
1167 auto guest
= WebViewGuest::FromWebContents(new_contents
);
1170 auto it
= pending_new_windows_
.find(guest
);
1171 if (it
== pending_new_windows_
.end())
1173 const NewWindowInfo
& new_window_info
= it
->second
;
1175 // Retrieve the opener partition info if we have it.
1176 const GURL
& site_url
= new_contents
->GetSiteInstance()->GetSiteURL();
1177 std::string storage_partition_id
= GetStoragePartitionIdFromSiteURL(site_url
);
1179 base::DictionaryValue request_info
;
1180 request_info
.SetInteger(webview::kInitialHeight
, initial_bounds
.height());
1181 request_info
.SetInteger(webview::kInitialWidth
, initial_bounds
.width());
1182 request_info
.Set(webview::kTargetURL
,
1183 new base::StringValue(new_window_info
.url
.spec()));
1184 request_info
.Set(webview::kName
, new base::StringValue(new_window_info
.name
));
1185 request_info
.SetInteger(webview::kWindowID
, guest
->guest_instance_id());
1186 // We pass in partition info so that window-s created through newwindow
1187 // API can use it to set their partition attribute.
1188 request_info
.Set(webview::kStoragePartitionId
,
1189 new base::StringValue(storage_partition_id
));
1191 webview::kWindowOpenDisposition
,
1192 new base::StringValue(WindowOpenDispositionToString(disposition
)));
1194 web_view_permission_helper_
->
1195 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
,
1197 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse
,
1198 weak_ptr_factory_
.GetWeakPtr(),
1199 guest
->guest_instance_id()),
1200 false /* allowed_by_default */);
1203 GURL
WebViewGuest::ResolveURL(const std::string
& src
) {
1204 if (!in_extension())
1207 GURL
default_url(base::StringPrintf("%s://%s/",
1209 owner_extension_id().c_str()));
1210 return default_url
.Resolve(src
);
1213 void WebViewGuest::OnWebViewNewWindowResponse(
1214 int new_window_instance_id
,
1216 const std::string
& user_input
) {
1218 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1219 new_window_instance_id
);
1227 } // namespace extensions