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_view/web_view_internal_api.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/browser/guest_view/guest_view_manager.h"
37 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
38 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
39 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
40 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
41 #include "extensions/common/constants.h"
42 #include "extensions/common/extension_messages.h"
43 #include "extensions/common/guest_view/guest_view_constants.h"
44 #include "extensions/strings/grit/extensions_strings.h"
45 #include "ipc/ipc_message_macros.h"
46 #include "net/base/escape.h"
47 #include "net/base/net_errors.h"
48 #include "ui/base/models/simple_menu_model.h"
50 using base::UserMetricsAction;
51 using content::RenderFrameHost;
52 using content::ResourceType;
53 using content::WebContents;
55 namespace extensions {
59 std::string WindowOpenDispositionToString(
60 WindowOpenDisposition window_open_disposition) {
61 switch (window_open_disposition) {
65 return "save_to_disk";
68 case NEW_BACKGROUND_TAB:
69 return "new_background_tab";
70 case NEW_FOREGROUND_TAB:
71 return "new_foreground_tab";
77 NOTREACHED() << "Unknown Window Open Disposition";
82 static std::string TerminationStatusToString(base::TerminationStatus status) {
84 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
86 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
87 case base::TERMINATION_STATUS_STILL_RUNNING:
89 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
91 case base::TERMINATION_STATUS_PROCESS_CRASHED:
93 case base::TERMINATION_STATUS_MAX_ENUM:
96 NOTREACHED() << "Unknown Termination Status.";
100 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
101 const std::string& partition_id = site_url.query();
102 bool persist_storage = site_url.path().find("persist") != std::string::npos;
103 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
106 void ParsePartitionParam(const base::DictionaryValue& create_params,
107 std::string* storage_partition_id,
108 bool* persist_storage) {
109 std::string partition_str;
110 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
114 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
115 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
116 // remove the prefix without splicing in the middle of a multi-byte codepoint.
117 // We can use the rest of the string as UTF-8 encoded one.
118 if (StartsWithASCII(partition_str, "persist:", true)) {
119 size_t index = partition_str.find(":");
120 CHECK(index != std::string::npos);
121 // It is safe to do index + 1, since we tested for the full prefix above.
122 *storage_partition_id = partition_str.substr(index + 1);
124 if (storage_partition_id->empty()) {
125 // TODO(lazyboy): Better way to deal with this error.
128 *persist_storage = true;
130 *storage_partition_id = partition_str;
131 *persist_storage = false;
138 GuestViewBase* WebViewGuest::Create(content::BrowserContext* browser_context,
139 int guest_instance_id) {
140 return new WebViewGuest(browser_context, guest_instance_id);
144 bool WebViewGuest::GetGuestPartitionConfigForSite(
146 std::string* partition_domain,
147 std::string* partition_name,
149 if (!site.SchemeIs(content::kGuestScheme))
152 // Since guest URLs are only used for packaged apps, there must be an app
154 CHECK(site.has_host());
155 *partition_domain = site.host();
156 // Since persistence is optional, the path must either be empty or the
158 *in_memory = (site.path() != "/persist");
159 // The partition name is user supplied value, which we have encoded when the
160 // URL was created, so it needs to be decoded.
162 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
167 const char WebViewGuest::Type[] = "webview";
170 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
171 WebViewGuest* guest = FromWebContents(contents);
173 return guestview::kInstanceIDNone;
175 return guest->view_instance_id();
178 const char* WebViewGuest::GetAPINamespace() const {
179 return webview::kAPINamespace;
182 int WebViewGuest::GetTaskPrefix() const {
183 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
186 void WebViewGuest::CreateWebContents(
187 const std::string& embedder_extension_id,
188 int embedder_render_process_id,
189 const GURL& embedder_site_url,
190 const base::DictionaryValue& create_params,
191 const WebContentsCreatedCallback& callback) {
192 content::RenderProcessHost* embedder_render_process_host =
193 content::RenderProcessHost::FromID(embedder_render_process_id);
194 std::string storage_partition_id;
195 bool persist_storage = false;
196 std::string storage_partition_string;
197 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
198 // Validate that the partition id coming from the renderer is valid UTF-8,
199 // since we depend on this in other parts of the code, such as FilePath
200 // creation. If the validation fails, treat it as a bad message and kill the
202 if (!base::IsStringUTF8(storage_partition_id)) {
203 content::RecordAction(
204 base::UserMetricsAction("BadMessageTerminate_BPGM"));
206 embedder_render_process_host->GetHandle(),
207 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
211 std::string url_encoded_partition = net::EscapeQueryParamValue(
212 storage_partition_id, false);
213 // The SiteInstance of a given webview tag is based on the fact that it's
214 // a guest process in addition to which platform application or which WebUI
215 // page the tag belongs to and what storage partition is in use, rather than
216 // the URL that the tag is being navigated to.
217 std::string partition_domain;
218 if (embedder_extension_id.empty()) {
219 DCHECK(content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
220 embedder_render_process_id));
221 partition_domain = embedder_site_url.host();
223 partition_domain = embedder_extension_id;
225 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
226 content::kGuestScheme,
227 partition_domain.c_str(),
228 persist_storage ? "persist" : "",
229 url_encoded_partition.c_str()));
231 // If we already have a webview tag in the same app using the same storage
232 // partition, we should use the same SiteInstance so the existing tag and
233 // the new tag can script each other.
234 GuestViewManager* guest_view_manager =
235 GuestViewManager::FromBrowserContext(
236 embedder_render_process_host->GetBrowserContext());
237 content::SiteInstance* guest_site_instance =
238 guest_view_manager->GetGuestSiteInstance(guest_site);
239 if (!guest_site_instance) {
240 // Create the SiteInstance in a new BrowsingInstance, which will ensure
241 // that webview tags are also not allowed to send messages across
242 // different partitions.
243 guest_site_instance = content::SiteInstance::CreateForURL(
244 embedder_render_process_host->GetBrowserContext(), guest_site);
246 WebContents::CreateParams params(
247 embedder_render_process_host->GetBrowserContext(),
248 guest_site_instance);
249 params.guest_delegate = this;
250 callback.Run(WebContents::Create(params));
253 void WebViewGuest::DidAttachToEmbedder() {
257 if (attach_params()->GetString(webview::kAttributeName, &name)) {
258 // If the guest window's name is empty, then the WebView tag's name is
259 // assigned. Otherwise, the guest window's name takes precedence over the
260 // WebView tag's name.
264 ReportFrameNameChange(name_);
266 std::string user_agent_override;
267 if (attach_params()->GetString(webview::kParameterUserAgentOverride,
268 &user_agent_override)) {
269 SetUserAgentOverride(user_agent_override);
271 SetUserAgentOverride("");
275 if (attach_params()->GetString(webview::kAttributeSrc, &src) && !src.empty())
279 // We need to do a navigation here if the target URL has changed between
280 // the time the WebContents was created and the time it was attached.
281 // We also need to do an initial navigation if a RenderView was never
282 // created for the new window in cases where there is no referrer.
283 PendingWindowMap::iterator it =
284 GetOpener()->pending_new_windows_.find(this);
285 if (it != GetOpener()->pending_new_windows_.end()) {
286 const NewWindowInfo& new_window_info = it->second;
287 if (new_window_info.changed || !web_contents()->HasOpener())
288 NavigateGuest(new_window_info.url.spec());
293 // Once a new guest is attached to the DOM of the embedder page, then the
294 // lifetime of the new guest is no longer managed by the opener guest.
295 GetOpener()->pending_new_windows_.erase(this);
298 bool allow_transparency = false;
299 attach_params()->GetBoolean(webview::kAttributeAllowTransparency,
300 &allow_transparency);
301 // We need to set the background opaque flag after navigation to ensure that
302 // there is a RenderWidgetHostView available.
303 SetAllowTransparency(allow_transparency);
305 if (web_view_guest_delegate_)
306 web_view_guest_delegate_->OnDidAttachToEmbedder();
309 void WebViewGuest::DidInitialize() {
310 script_executor_.reset(
311 new ScriptExecutor(web_contents(), &script_observers_));
313 notification_registrar_.Add(this,
314 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
315 content::Source<WebContents>(web_contents()));
317 notification_registrar_.Add(this,
318 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
319 content::Source<WebContents>(web_contents()));
321 if (web_view_guest_delegate_)
322 web_view_guest_delegate_->OnDidInitialize();
323 AttachWebViewHelpers(web_contents());
326 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
327 if (web_view_guest_delegate_)
328 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
329 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
332 void WebViewGuest::DidStopLoading() {
333 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
334 DispatchEventToEmbedder(
335 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
338 void WebViewGuest::EmbedderDestroyed() {
339 if (web_view_guest_delegate_)
340 web_view_guest_delegate_->OnEmbedderDestroyed();
343 void WebViewGuest::GuestDestroyed() {
344 // Clean up custom context menu items for this guest.
345 if (web_view_guest_delegate_)
346 web_view_guest_delegate_->OnGuestDestroyed();
347 RemoveWebViewStateFromIOThread(web_contents());
350 void WebViewGuest::GuestReady() {
351 // The guest RenderView should always live in an isolated guest process.
352 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
353 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
355 // We don't want to accidentally set the opacity of an interstitial page.
356 // WebContents::GetRenderWidgetHostView will return the RWHV of an
357 // interstitial page if one is showing at this time. We only want opacity
358 // to apply to web pages.
359 web_contents()->GetRenderViewHost()->GetView()->
360 SetBackgroundOpaque(guest_opaque_);
363 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
364 const gfx::Size& new_size) {
365 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
366 args->SetInteger(webview::kOldHeight, old_size.height());
367 args->SetInteger(webview::kOldWidth, old_size.width());
368 args->SetInteger(webview::kNewHeight, new_size.height());
369 args->SetInteger(webview::kNewWidth, new_size.width());
370 DispatchEventToEmbedder(
371 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
374 bool WebViewGuest::IsAutoSizeSupported() const {
378 bool WebViewGuest::IsDragAndDropEnabled() const {
382 void WebViewGuest::WillDestroy() {
383 if (!attached() && GetOpener())
384 GetOpener()->pending_new_windows_.erase(this);
385 DestroyUnattachedWindows();
387 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
388 DispatchEventToEmbedder(
389 new GuestViewBase::Event(webview::kEventPluginDestroyed, args.Pass()));
392 bool WebViewGuest::AddMessageToConsole(WebContents* source,
394 const base::string16& message,
396 const base::string16& source_id) {
397 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
398 // Log levels are from base/logging.h: LogSeverity.
399 args->SetInteger(webview::kLevel, level);
400 args->SetString(webview::kMessage, message);
401 args->SetInteger(webview::kLine, line_no);
402 args->SetString(webview::kSourceId, source_id);
403 DispatchEventToEmbedder(
404 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
408 void WebViewGuest::CloseContents(WebContents* source) {
409 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
410 DispatchEventToEmbedder(
411 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
414 void WebViewGuest::FindReply(WebContents* source,
416 int number_of_matches,
417 const gfx::Rect& selection_rect,
418 int active_match_ordinal,
420 find_helper_.FindReply(request_id,
423 active_match_ordinal,
427 bool WebViewGuest::HandleContextMenu(
428 const content::ContextMenuParams& params) {
429 if (!web_view_guest_delegate_)
431 return web_view_guest_delegate_->HandleContextMenu(params);
434 void WebViewGuest::HandleKeyboardEvent(
436 const content::NativeWebKeyboardEvent& event) {
440 if (HandleKeyboardShortcuts(event))
443 // Send the unhandled keyboard events back to the embedder to reprocess them.
444 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
445 // events because the guest may be arbitrarily delayed when responding to
446 // keyboard events. In that time, the embedder may have received and processed
447 // additional key events. This needs to be fixed as soon as possible.
448 // See http://crbug.com/229882.
449 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
450 web_contents(), event);
453 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
455 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
456 args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
457 args->SetDouble(webview::kProgress, progress);
458 DispatchEventToEmbedder(
459 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
462 void WebViewGuest::LoadAbort(bool is_top_level,
464 const std::string& error_type) {
465 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
466 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
467 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
468 args->SetString(guestview::kReason, error_type);
469 DispatchEventToEmbedder(
470 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
473 void WebViewGuest::OnFrameNameChanged(bool is_top_level,
474 const std::string& name) {
481 ReportFrameNameChange(name);
484 void WebViewGuest::CreateNewGuestWebViewWindow(
485 const content::OpenURLParams& params) {
486 GuestViewManager* guest_manager =
487 GuestViewManager::FromBrowserContext(browser_context());
488 // Set the attach params to use the same partition as the opener.
489 // We pull the partition information from the site's URL, which is of the
490 // form guest://site/{persist}?{partition_name}.
491 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
492 const std::string storage_partition_id =
493 GetStoragePartitionIdFromSiteURL(site_url);
494 base::DictionaryValue create_params;
495 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
497 guest_manager->CreateGuest(WebViewGuest::Type,
498 embedder_extension_id(),
499 embedder_web_contents(),
501 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
502 base::Unretained(this),
506 void WebViewGuest::NewGuestWebViewCallback(
507 const content::OpenURLParams& params,
508 content::WebContents* guest_web_contents) {
509 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
510 new_guest->SetOpener(this);
512 // Take ownership of |new_guest|.
513 pending_new_windows_.insert(
514 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
516 // Request permission to show the new window.
517 RequestNewWindowPermission(params.disposition,
520 new_guest->web_contents());
523 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
524 // 'unresponsive' events.
525 void WebViewGuest::RendererResponsive(content::WebContents* source) {
526 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
527 args->SetInteger(webview::kProcessId,
528 web_contents()->GetRenderProcessHost()->GetID());
529 DispatchEventToEmbedder(
530 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
533 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
534 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
535 args->SetInteger(webview::kProcessId,
536 web_contents()->GetRenderProcessHost()->GetID());
537 DispatchEventToEmbedder(
538 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
541 void WebViewGuest::Observe(int type,
542 const content::NotificationSource& source,
543 const content::NotificationDetails& details) {
545 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
546 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
547 if (content::Source<WebContents>(source).ptr() == web_contents())
551 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
552 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
553 content::ResourceRedirectDetails* resource_redirect_details =
554 content::Details<content::ResourceRedirectDetails>(details).ptr();
555 bool is_top_level = resource_redirect_details->resource_type ==
556 content::RESOURCE_TYPE_MAIN_FRAME;
557 LoadRedirect(resource_redirect_details->url,
558 resource_redirect_details->new_url,
563 NOTREACHED() << "Unexpected notification sent.";
568 double WebViewGuest::GetZoom() {
569 if (!web_view_guest_delegate_)
571 return web_view_guest_delegate_->GetZoom();
574 void WebViewGuest::Find(
575 const base::string16& search_text,
576 const blink::WebFindOptions& options,
577 scoped_refptr<WebViewInternalFindFunction> find_function) {
578 find_helper_.Find(web_contents(), search_text, options, find_function);
581 void WebViewGuest::StopFinding(content::StopFindAction action) {
582 find_helper_.CancelAllFindSessions();
583 web_contents()->StopFinding(action);
586 void WebViewGuest::Go(int relative_index) {
587 web_contents()->GetController().GoToOffset(relative_index);
590 void WebViewGuest::Reload() {
591 // TODO(fsamuel): Don't check for repost because we don't want to show
592 // Chromium's repost warning. We might want to implement a separate API
593 // for registering a callback if a repost is about to happen.
594 web_contents()->GetController().Reload(false);
597 void WebViewGuest::SetUserAgentOverride(
598 const std::string& user_agent_override) {
601 is_overriding_user_agent_ = !user_agent_override.empty();
602 if (is_overriding_user_agent_) {
603 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
605 web_contents()->SetUserAgentOverride(user_agent_override);
608 void WebViewGuest::Stop() {
609 web_contents()->Stop();
612 void WebViewGuest::Terminate() {
613 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
614 base::ProcessHandle process_handle =
615 web_contents()->GetRenderProcessHost()->GetHandle();
617 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
620 bool WebViewGuest::ClearData(const base::Time remove_since,
622 const base::Closure& callback) {
623 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
624 content::StoragePartition* partition =
625 content::BrowserContext::GetStoragePartition(
626 web_contents()->GetBrowserContext(),
627 web_contents()->GetSiteInstance());
632 partition->ClearData(
634 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
636 content::StoragePartition::OriginMatcherFunction(),
643 WebViewGuest::WebViewGuest(content::BrowserContext* browser_context,
644 int guest_instance_id)
645 : GuestView<WebViewGuest>(browser_context, guest_instance_id),
647 is_overriding_user_agent_(false),
649 javascript_dialog_helper_(this) {
650 web_view_guest_delegate_.reset(
651 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
654 WebViewGuest::~WebViewGuest() {
657 void WebViewGuest::DidCommitProvisionalLoadForFrame(
658 content::RenderFrameHost* render_frame_host,
660 ui::PageTransition transition_type) {
661 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
662 args->SetString(guestview::kUrl, url.spec());
663 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
664 args->SetString(webview::kInternalBaseURLForDataURL,
667 .GetLastCommittedEntry()
668 ->GetBaseURLForDataURL()
670 args->SetInteger(webview::kInternalCurrentEntryIndex,
671 web_contents()->GetController().GetCurrentEntryIndex());
672 args->SetInteger(webview::kInternalEntryCount,
673 web_contents()->GetController().GetEntryCount());
674 args->SetInteger(webview::kInternalProcessId,
675 web_contents()->GetRenderProcessHost()->GetID());
676 DispatchEventToEmbedder(
677 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
679 find_helper_.CancelAllFindSessions();
680 if (web_view_guest_delegate_) {
681 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
682 !render_frame_host->GetParent());
686 void WebViewGuest::DidFailProvisionalLoad(
687 content::RenderFrameHost* render_frame_host,
688 const GURL& validated_url,
690 const base::string16& error_description) {
691 LoadAbort(!render_frame_host->GetParent(), validated_url,
692 net::ErrorToShortString(error_code));
695 void WebViewGuest::DidStartProvisionalLoadForFrame(
696 content::RenderFrameHost* render_frame_host,
697 const GURL& validated_url,
699 bool is_iframe_srcdoc) {
700 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
701 args->SetString(guestview::kUrl, validated_url.spec());
702 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
703 DispatchEventToEmbedder(
704 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
707 void WebViewGuest::DocumentLoadedInFrame(
708 content::RenderFrameHost* render_frame_host) {
709 if (web_view_guest_delegate_)
710 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
713 bool WebViewGuest::OnMessageReceived(const IPC::Message& message,
714 RenderFrameHost* render_frame_host) {
716 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message)
717 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged)
718 IPC_MESSAGE_UNHANDLED(handled = false)
719 IPC_END_MESSAGE_MAP()
723 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
724 // Cancel all find sessions in progress.
725 find_helper_.CancelAllFindSessions();
727 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
728 args->SetInteger(webview::kProcessId,
729 web_contents()->GetRenderProcessHost()->GetID());
730 args->SetString(webview::kReason, TerminationStatusToString(status));
731 DispatchEventToEmbedder(
732 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
735 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);
750 DispatchEventToEmbedder(
751 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
754 void WebViewGuest::LoadHandlerCalled() {
755 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
756 DispatchEventToEmbedder(
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());
767 DispatchEventToEmbedder(
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 = embedder_render_process_id();
784 web_view_info.instance_id = view_instance_id();
785 web_view_info.partition_id = partition_id;
786 web_view_info.embedder_extension_id = embedder_extension_id();
788 content::BrowserThread::PostTask(
789 content::BrowserThread::IO,
791 base::Bind(&WebViewRendererState::AddGuest,
792 base::Unretained(WebViewRendererState::GetInstance()),
793 web_contents()->GetRenderProcessHost()->GetID(),
794 web_contents()->GetRoutingID(),
799 void WebViewGuest::RemoveWebViewStateFromIOThread(
800 WebContents* web_contents) {
801 content::BrowserThread::PostTask(
802 content::BrowserThread::IO, FROM_HERE,
804 &WebViewRendererState::RemoveGuest,
805 base::Unretained(WebViewRendererState::GetInstance()),
806 web_contents->GetRenderProcessHost()->GetID(),
807 web_contents->GetRoutingID()));
810 content::WebContents* WebViewGuest::CreateNewGuestWindow(
811 const content::WebContents::CreateParams& create_params) {
812 GuestViewManager* guest_manager =
813 GuestViewManager::FromBrowserContext(browser_context());
814 return guest_manager->CreateGuestWithWebContentsParams(
816 embedder_extension_id(),
817 embedder_web_contents()->GetRenderProcessHost()->GetID(),
821 void WebViewGuest::RequestMediaAccessPermission(
822 content::WebContents* source,
823 const content::MediaStreamRequest& request,
824 const content::MediaResponseCallback& callback) {
825 web_view_permission_helper_->RequestMediaAccessPermission(source,
830 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
831 const GURL& security_origin,
832 content::MediaStreamType type) {
833 return web_view_permission_helper_->CheckMediaAccessPermission(
834 source, security_origin, type);
837 void WebViewGuest::CanDownload(
838 content::RenderViewHost* render_view_host,
840 const std::string& request_method,
841 const base::Callback<void(bool)>& callback) {
842 web_view_permission_helper_->CanDownload(render_view_host,
848 void WebViewGuest::RequestPointerLockPermission(
850 bool last_unlocked_by_target,
851 const base::Callback<void(bool)>& callback) {
852 web_view_permission_helper_->RequestPointerLockPermission(
854 last_unlocked_by_target,
858 void WebViewGuest::WillAttachToEmbedder() {
859 // We must install the mapping from guests to WebViews prior to resuming
860 // suspended resource loads so that the WebRequest API will catch resource
862 PushWebViewStateToIOThread();
865 content::JavaScriptDialogManager*
866 WebViewGuest::GetJavaScriptDialogManager() {
867 return &javascript_dialog_helper_;
870 content::ColorChooser* WebViewGuest::OpenColorChooser(
871 WebContents* web_contents,
873 const std::vector<content::ColorSuggestion>& suggestions) {
874 if (!attached() || !embedder_web_contents()->GetDelegate())
876 return embedder_web_contents()->GetDelegate()->OpenColorChooser(
877 web_contents, color, suggestions);
880 void WebViewGuest::NavigateGuest(const std::string& src) {
884 GURL url = ResolveURL(src);
886 // Do not allow navigating a guest to schemes other than known safe schemes.
887 // This will block the embedder trying to load unwanted schemes, e.g.
888 // chrome://settings.
889 bool scheme_is_blocked =
890 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
892 !url.SchemeIs(url::kAboutScheme)) ||
893 url.SchemeIs(url::kJavaScriptScheme);
894 if (scheme_is_blocked || !url.is_valid()) {
895 LoadAbort(true /* is_top_level */, url,
896 net::ErrorToShortString(net::ERR_ABORTED));
900 GURL validated_url(url);
901 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
902 // As guests do not swap processes on navigation, only navigations to
903 // normal web URLs are supported. No protocol handlers are installed for
904 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
905 // can be granted to the guest process.
906 LoadURLWithParams(validated_url,
908 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
912 bool WebViewGuest::HandleKeyboardShortcuts(
913 const content::NativeWebKeyboardEvent& event) {
914 if (event.type != blink::WebInputEvent::RawKeyDown)
917 // If the user hits the escape key without any modifiers then unlock the
918 // mouse if necessary.
919 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
920 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
921 return web_contents()->GotResponseToLockMouseRequest(false);
924 #if defined(OS_MACOSX)
925 if (event.modifiers != blink::WebInputEvent::MetaKey)
928 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
933 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
938 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
943 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
952 void WebViewGuest::SetUpAutoSize() {
953 // Read the autosize parameters passed in from the embedder.
954 bool auto_size_enabled = false;
955 attach_params()->GetBoolean(webview::kAttributeAutoSize, &auto_size_enabled);
959 attach_params()->GetInteger(webview::kAttributeMaxHeight, &max_height);
960 attach_params()->GetInteger(webview::kAttributeMaxWidth, &max_width);
964 attach_params()->GetInteger(webview::kAttributeMinHeight, &min_height);
965 attach_params()->GetInteger(webview::kAttributeMinWidth, &min_width);
967 // Call SetAutoSize to apply all the appropriate validation and clipping of
969 SetAutoSize(auto_size_enabled,
970 gfx::Size(min_width, min_height),
971 gfx::Size(max_width, max_height));
974 void WebViewGuest::ShowContextMenu(
976 const WebViewGuestDelegate::MenuItemVector* items) {
977 if (web_view_guest_delegate_)
978 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
981 void WebViewGuest::SetName(const std::string& name) {
986 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
989 void WebViewGuest::SetZoom(double zoom_factor) {
990 if (web_view_guest_delegate_)
991 web_view_guest_delegate_->OnSetZoom(zoom_factor);
994 void WebViewGuest::SetAllowTransparency(bool allow) {
995 if (guest_opaque_ != allow)
998 guest_opaque_ = !allow;
999 if (!web_contents()->GetRenderViewHost()->GetView())
1002 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundOpaque(!allow);
1005 void WebViewGuest::AddNewContents(content::WebContents* source,
1006 content::WebContents* new_contents,
1007 WindowOpenDisposition disposition,
1008 const gfx::Rect& initial_pos,
1010 bool* was_blocked) {
1012 *was_blocked = false;
1013 RequestNewWindowPermission(disposition,
1019 content::WebContents* WebViewGuest::OpenURLFromTab(
1020 content::WebContents* source,
1021 const content::OpenURLParams& params) {
1022 // If the guest wishes to navigate away prior to attachment then we save the
1023 // navigation to perform upon attachment. Navigation initializes a lot of
1024 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1025 // Navigation also resumes resource loading which we don't want to allow
1026 // until attachment.
1028 WebViewGuest* opener = GetOpener();
1029 PendingWindowMap::iterator it =
1030 opener->pending_new_windows_.find(this);
1031 if (it == opener->pending_new_windows_.end())
1033 const NewWindowInfo& info = it->second;
1034 NewWindowInfo new_window_info(params.url, info.name);
1035 new_window_info.changed = new_window_info.url != info.url;
1036 it->second = new_window_info;
1039 if (params.disposition == CURRENT_TAB) {
1040 // This can happen for cross-site redirects.
1041 LoadURLWithParams(params.url, params.referrer, params.transition, source);
1045 CreateNewGuestWebViewWindow(params);
1049 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1050 int opener_render_frame_id,
1051 const base::string16& frame_name,
1052 const GURL& target_url,
1053 content::WebContents* new_contents) {
1054 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1056 guest->SetOpener(this);
1057 std::string guest_name = base::UTF16ToUTF8(frame_name);
1058 guest->name_ = guest_name;
1059 pending_new_windows_.insert(
1060 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1063 void WebViewGuest::LoadURLWithParams(const GURL& url,
1064 const content::Referrer& referrer,
1065 ui::PageTransition transition_type,
1066 content::WebContents* web_contents) {
1067 content::NavigationController::LoadURLParams load_url_params(url);
1068 load_url_params.referrer = referrer;
1069 load_url_params.transition_type = transition_type;
1070 load_url_params.extra_headers = std::string();
1071 if (is_overriding_user_agent_) {
1072 load_url_params.override_user_agent =
1073 content::NavigationController::UA_OVERRIDE_TRUE;
1075 web_contents->GetController().LoadURLWithParams(load_url_params);
1078 void WebViewGuest::RequestNewWindowPermission(
1079 WindowOpenDisposition disposition,
1080 const gfx::Rect& initial_bounds,
1082 content::WebContents* new_contents) {
1083 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1086 PendingWindowMap::iterator it = pending_new_windows_.find(guest);
1087 if (it == pending_new_windows_.end())
1089 const NewWindowInfo& new_window_info = it->second;
1091 // Retrieve the opener partition info if we have it.
1092 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1093 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1095 base::DictionaryValue request_info;
1096 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1097 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1098 request_info.Set(webview::kTargetURL,
1099 new base::StringValue(new_window_info.url.spec()));
1100 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1101 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1102 // We pass in partition info so that window-s created through newwindow
1103 // API can use it to set their partition attribute.
1104 request_info.Set(webview::kStoragePartitionId,
1105 new base::StringValue(storage_partition_id));
1107 webview::kWindowOpenDisposition,
1108 new base::StringValue(WindowOpenDispositionToString(disposition)));
1110 web_view_permission_helper_->
1111 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1113 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1114 base::Unretained(this),
1115 guest->guest_instance_id()),
1116 false /* allowed_by_default */);
1119 void WebViewGuest::DestroyUnattachedWindows() {
1120 // Destroy() reaches in and removes the WebViewGuest from its opener's
1121 // pending_new_windows_ set. To avoid mutating the set while iterating, we
1122 // create a copy of the pending new windows set and iterate over the copy.
1123 PendingWindowMap pending_new_windows(pending_new_windows_);
1124 // Clean up unattached new windows opened by this guest.
1125 for (PendingWindowMap::const_iterator it = pending_new_windows.begin();
1126 it != pending_new_windows.end(); ++it) {
1127 it->first->Destroy();
1129 // All pending windows should be removed from the set after Destroy() is
1130 // called on all of them.
1131 DCHECK(pending_new_windows_.empty());
1134 GURL WebViewGuest::ResolveURL(const std::string& src) {
1135 if (!in_extension()) {
1139 GURL default_url(base::StringPrintf("%s://%s/",
1141 embedder_extension_id().c_str()));
1142 return default_url.Resolve(src);
1145 void WebViewGuest::OnWebViewNewWindowResponse(
1146 int new_window_instance_id,
1148 const std::string& user_input) {
1149 WebViewGuest* guest =
1150 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id);
1158 } // namespace extensions