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 "components/browsing_data/storage_partition_http_cache_data_remover.h"
11 #include "components/guest_view/browser/guest_view_event.h"
12 #include "components/guest_view/browser/guest_view_manager.h"
13 #include "components/guest_view/common/guest_view_constants.h"
14 #include "components/web_cache/browser/web_cache_manager.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_security_policy.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/browser/resource_request_details.h"
28 #include "content/public/browser/site_instance.h"
29 #include "content/public/browser/storage_partition.h"
30 #include "content/public/browser/user_metrics.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/common/media_stream_request.h"
34 #include "content/public/common/page_zoom.h"
35 #include "content/public/common/result_codes.h"
36 #include "content/public/common/stop_find_action.h"
37 #include "content/public/common/url_constants.h"
38 #include "extensions/browser/api/declarative/rules_registry_service.h"
39 #include "extensions/browser/api/extensions_api_client.h"
40 #include "extensions/browser/api/guest_view/web_view/web_view_internal_api.h"
41 #include "extensions/browser/api/web_request/web_request_api.h"
42 #include "extensions/browser/extension_system.h"
43 #include "extensions/browser/extensions_browser_client.h"
44 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
45 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
46 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
47 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
48 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
49 #include "extensions/common/constants.h"
50 #include "extensions/common/extension_messages.h"
51 #include "extensions/strings/grit/extensions_strings.h"
52 #include "ipc/ipc_message_macros.h"
53 #include "net/base/escape.h"
54 #include "net/base/net_errors.h"
55 #include "ui/base/models/simple_menu_model.h"
56 #include "url/url_constants.h"
58 using base::UserMetricsAction
;
59 using content::GlobalRequestID
;
60 using content::RenderFrameHost
;
61 using content::ResourceType
;
62 using content::StoragePartition
;
63 using content::WebContents
;
64 using guest_view::GuestViewBase
;
65 using guest_view::GuestViewEvent
;
66 using guest_view::GuestViewManager
;
67 using ui_zoom::ZoomController
;
69 namespace extensions
{
73 // Returns storage partition removal mask from web_view clearData mask. Note
74 // that storage partition mask is a subset of webview's data removal mask.
75 uint32
GetStoragePartitionRemovalMask(uint32 web_view_removal_mask
) {
77 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_APPCACHE
)
78 mask
|= StoragePartition::REMOVE_DATA_MASK_APPCACHE
;
79 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_COOKIES
)
80 mask
|= StoragePartition::REMOVE_DATA_MASK_COOKIES
;
81 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS
)
82 mask
|= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS
;
83 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB
)
84 mask
|= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB
;
85 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE
)
86 mask
|= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE
;
87 if (web_view_removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL
)
88 mask
|= StoragePartition::REMOVE_DATA_MASK_WEBSQL
;
93 std::string
WindowOpenDispositionToString(
94 WindowOpenDisposition window_open_disposition
) {
95 switch (window_open_disposition
) {
99 return "save_to_disk";
101 return "current_tab";
102 case NEW_BACKGROUND_TAB
:
103 return "new_background_tab";
104 case NEW_FOREGROUND_TAB
:
105 return "new_foreground_tab";
111 NOTREACHED() << "Unknown Window Open Disposition";
116 static std::string
TerminationStatusToString(base::TerminationStatus status
) {
118 case base::TERMINATION_STATUS_NORMAL_TERMINATION
:
120 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
121 case base::TERMINATION_STATUS_STILL_RUNNING
:
123 #if defined(OS_CHROMEOS)
124 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM
:
127 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
129 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
131 case base::TERMINATION_STATUS_MAX_ENUM
:
134 NOTREACHED() << "Unknown Termination Status.";
138 std::string
GetStoragePartitionIdFromSiteURL(const GURL
& site_url
) {
139 const std::string
& partition_id
= site_url
.query();
140 bool persist_storage
= site_url
.path().find("persist") != std::string::npos
;
141 return (persist_storage
? webview::kPersistPrefix
: "") + partition_id
;
144 void ParsePartitionParam(const base::DictionaryValue
& create_params
,
145 std::string
* storage_partition_id
,
146 bool* persist_storage
) {
147 std::string partition_str
;
148 if (!create_params
.GetString(webview::kStoragePartitionId
, &partition_str
)) {
152 // Since the "persist:" prefix is in ASCII, base::StartsWith will work fine on
153 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
154 // remove the prefix without splicing in the middle of a multi-byte codepoint.
155 // We can use the rest of the string as UTF-8 encoded one.
156 if (base::StartsWith(partition_str
, "persist:",
157 base::CompareCase::SENSITIVE
)) {
158 size_t index
= partition_str
.find(":");
159 CHECK(index
!= std::string::npos
);
160 // It is safe to do index + 1, since we tested for the full prefix above.
161 *storage_partition_id
= partition_str
.substr(index
+ 1);
163 if (storage_partition_id
->empty()) {
164 // TODO(lazyboy): Better way to deal with this error.
167 *persist_storage
= true;
169 *storage_partition_id
= partition_str
;
170 *persist_storage
= false;
174 void RemoveWebViewEventListenersOnIOThread(
176 int embedder_process_id
,
177 int view_instance_id
) {
178 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
179 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
185 double ConvertZoomLevelToZoomFactor(double zoom_level
) {
186 double zoom_factor
= content::ZoomLevelToZoomFactor(zoom_level
);
187 // Because the conversion from zoom level to zoom factor isn't perfect, the
188 // resulting zoom factor is rounded to the nearest 6th decimal place.
189 zoom_factor
= round(zoom_factor
* 1000000) / 1000000;
193 using WebViewKey
= std::pair
<int, int>;
194 using WebViewKeyToIDMap
= std::map
<WebViewKey
, int>;
195 static base::LazyInstance
<WebViewKeyToIDMap
> web_view_key_to_id_map
=
196 LAZY_INSTANCE_INITIALIZER
;
201 void WebViewGuest::CleanUp(content::BrowserContext
* browser_context
,
202 int embedder_process_id
,
203 int view_instance_id
) {
204 GuestViewBase::CleanUp(browser_context
, embedder_process_id
,
207 // Clean up rules registries for the WebView.
208 WebViewKey
key(embedder_process_id
, view_instance_id
);
209 auto it
= web_view_key_to_id_map
.Get().find(key
);
210 if (it
!= web_view_key_to_id_map
.Get().end()) {
211 auto rules_registry_id
= it
->second
;
212 web_view_key_to_id_map
.Get().erase(it
);
213 RulesRegistryService
* rrs
=
214 RulesRegistryService::GetIfExists(browser_context
);
216 rrs
->RemoveRulesRegistriesByID(rules_registry_id
);
219 // Clean up web request event listeners for the WebView.
220 content::BrowserThread::PostTask(
221 content::BrowserThread::IO
,
224 &RemoveWebViewEventListenersOnIOThread
,
229 // Clean up content scripts for the WebView.
230 auto csm
= WebViewContentScriptManager::Get(browser_context
);
231 csm
->RemoveAllContentScriptsForWebView(embedder_process_id
, view_instance_id
);
233 // Allow an extensions browser client to potentially perform more cleanup.
234 ExtensionsBrowserClient::Get()->CleanUpWebView(
235 browser_context
, embedder_process_id
, view_instance_id
);
239 GuestViewBase
* WebViewGuest::Create(content::WebContents
* owner_web_contents
) {
240 return new WebViewGuest(owner_web_contents
);
244 bool WebViewGuest::GetGuestPartitionConfigForSite(
246 std::string
* partition_domain
,
247 std::string
* partition_name
,
249 if (!site
.SchemeIs(content::kGuestScheme
))
252 // Since guest URLs are only used for packaged apps, there must be an app
254 CHECK(site
.has_host());
255 *partition_domain
= site
.host();
256 // Since persistence is optional, the path must either be empty or the
258 *in_memory
= (site
.path() != "/persist");
259 // The partition name is user supplied value, which we have encoded when the
260 // URL was created, so it needs to be decoded.
262 net::UnescapeURLComponent(site
.query(), net::UnescapeRule::NORMAL
);
267 const char WebViewGuest::Type
[] = "webview";
270 int WebViewGuest::GetOrGenerateRulesRegistryID(
271 int embedder_process_id
,
272 int webview_instance_id
) {
273 bool is_web_view
= embedder_process_id
&& webview_instance_id
;
275 return RulesRegistryService::kDefaultRulesRegistryID
;
277 WebViewKey key
= std::make_pair(embedder_process_id
, webview_instance_id
);
278 auto it
= web_view_key_to_id_map
.Get().find(key
);
279 if (it
!= web_view_key_to_id_map
.Get().end())
282 auto rph
= content::RenderProcessHost::FromID(embedder_process_id
);
283 int rules_registry_id
=
284 RulesRegistryService::Get(rph
->GetBrowserContext())->
285 GetNextRulesRegistryID();
286 web_view_key_to_id_map
.Get()[key
] = rules_registry_id
;
287 return rules_registry_id
;
290 bool WebViewGuest::CanRunInDetachedState() const {
294 void WebViewGuest::CreateWebContents(
295 const base::DictionaryValue
& create_params
,
296 const WebContentsCreatedCallback
& callback
) {
297 content::RenderProcessHost
* owner_render_process_host
=
298 owner_web_contents()->GetRenderProcessHost();
299 std::string storage_partition_id
;
300 bool persist_storage
= false;
301 ParsePartitionParam(create_params
, &storage_partition_id
, &persist_storage
);
302 // Validate that the partition id coming from the renderer is valid UTF-8,
303 // since we depend on this in other parts of the code, such as FilePath
304 // creation. If the validation fails, treat it as a bad message and kill the
306 if (!base::IsStringUTF8(storage_partition_id
)) {
307 content::RecordAction(
308 base::UserMetricsAction("BadMessageTerminate_BPGM"));
309 owner_render_process_host
->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE
,
311 callback
.Run(nullptr);
314 std::string url_encoded_partition
= net::EscapeQueryParamValue(
315 storage_partition_id
, false);
316 std::string partition_domain
= GetOwnerSiteURL().host();
317 GURL
guest_site(base::StringPrintf("%s://%s/%s?%s",
318 content::kGuestScheme
,
319 partition_domain
.c_str(),
320 persist_storage
? "persist" : "",
321 url_encoded_partition
.c_str()));
323 // If we already have a webview tag in the same app using the same storage
324 // partition, we should use the same SiteInstance so the existing tag and
325 // the new tag can script each other.
326 auto guest_view_manager
= GuestViewManager::FromBrowserContext(
327 owner_render_process_host
->GetBrowserContext());
328 content::SiteInstance
* guest_site_instance
=
329 guest_view_manager
->GetGuestSiteInstance(guest_site
);
330 if (!guest_site_instance
) {
331 // Create the SiteInstance in a new BrowsingInstance, which will ensure
332 // that webview tags are also not allowed to send messages across
333 // different partitions.
334 guest_site_instance
= content::SiteInstance::CreateForURL(
335 owner_render_process_host
->GetBrowserContext(), guest_site
);
337 WebContents::CreateParams
params(
338 owner_render_process_host
->GetBrowserContext(),
339 guest_site_instance
);
340 params
.guest_delegate
= this;
341 callback
.Run(WebContents::Create(params
));
344 void WebViewGuest::DidAttachToEmbedder() {
345 ApplyAttributes(*attach_params());
348 void WebViewGuest::DidDropLink(const GURL
& url
) {
349 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
350 args
->SetString(guest_view::kUrl
, url
.spec());
352 new GuestViewEvent(webview::kEventDropLink
, args
.Pass()));
355 void WebViewGuest::DidInitialize(const base::DictionaryValue
& create_params
) {
356 script_executor_
.reset(
357 new ScriptExecutor(web_contents(), &script_observers_
));
359 notification_registrar_
.Add(this,
360 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
,
361 content::Source
<WebContents
>(web_contents()));
363 notification_registrar_
.Add(this,
364 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
365 content::Source
<WebContents
>(web_contents()));
367 if (web_view_guest_delegate_
)
368 web_view_guest_delegate_
->OnDidInitialize();
369 ExtensionsAPIClient::Get()->AttachWebContentsHelpers(web_contents());
370 web_view_permission_helper_
.reset(new WebViewPermissionHelper(this));
372 rules_registry_id_
= GetOrGenerateRulesRegistryID(
373 owner_web_contents()->GetRenderProcessHost()->GetID(),
376 // We must install the mapping from guests to WebViews prior to resuming
377 // suspended resource loads so that the WebRequest API will catch resource
379 PushWebViewStateToIOThread();
381 ApplyAttributes(create_params
);
384 void WebViewGuest::ClearDataInternal(base::Time remove_since
,
386 const base::Closure
& callback
) {
387 uint32 storage_partition_removal_mask
=
388 GetStoragePartitionRemovalMask(removal_mask
);
389 if (!storage_partition_removal_mask
) {
393 content::StoragePartition
* partition
=
394 content::BrowserContext::GetStoragePartition(
395 web_contents()->GetBrowserContext(),
396 web_contents()->GetSiteInstance());
397 partition
->ClearData(
398 storage_partition_removal_mask
,
399 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL
, GURL(),
400 content::StoragePartition::OriginMatcherFunction(), remove_since
,
401 base::Time::Now(), callback
);
404 void WebViewGuest::GuestViewDidStopLoading() {
405 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
407 new GuestViewEvent(webview::kEventLoadStop
, args
.Pass()));
410 void WebViewGuest::EmbedderFullscreenToggled(bool entered_fullscreen
) {
411 is_embedder_fullscreen_
= entered_fullscreen
;
412 // If the embedder has got out of fullscreen, we get out of fullscreen
414 if (!entered_fullscreen
)
415 SetFullscreenState(false);
418 const char* WebViewGuest::GetAPINamespace() const {
419 return webview::kAPINamespace
;
422 int WebViewGuest::GetTaskPrefix() const {
423 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX
;
426 void WebViewGuest::GuestDestroyed() {
427 RemoveWebViewStateFromIOThread(web_contents());
430 void WebViewGuest::GuestReady() {
431 // The guest RenderView should always live in an isolated guest process.
432 CHECK(web_contents()->GetRenderProcessHost()->IsForGuestsOnly());
433 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_
));
435 // We don't want to accidentally set the opacity of an interstitial page.
436 // WebContents::GetRenderWidgetHostView will return the RWHV of an
437 // interstitial page if one is showing at this time. We only want opacity
438 // to apply to web pages.
439 if (allow_transparency_
) {
440 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
441 SK_ColorTRANSPARENT
);
444 ->GetRenderViewHost()
446 ->SetBackgroundColorToDefault();
450 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size
& old_size
,
451 const gfx::Size
& new_size
) {
452 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
453 args
->SetInteger(webview::kOldHeight
, old_size
.height());
454 args
->SetInteger(webview::kOldWidth
, old_size
.width());
455 args
->SetInteger(webview::kNewHeight
, new_size
.height());
456 args
->SetInteger(webview::kNewWidth
, new_size
.width());
458 new GuestViewEvent(webview::kEventSizeChanged
, args
.Pass()));
461 bool WebViewGuest::IsAutoSizeSupported() const {
465 void WebViewGuest::GuestZoomChanged(double old_zoom_level
,
466 double new_zoom_level
) {
467 // Dispatch the zoomchange event.
468 double old_zoom_factor
= ConvertZoomLevelToZoomFactor(old_zoom_level
);
469 double new_zoom_factor
= ConvertZoomLevelToZoomFactor(new_zoom_level
);
470 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
471 args
->SetDouble(webview::kOldZoomFactor
, old_zoom_factor
);
472 args
->SetDouble(webview::kNewZoomFactor
, new_zoom_factor
);
474 new GuestViewEvent(webview::kEventZoomChange
, args
.Pass()));
477 void WebViewGuest::WillDestroy() {
478 if (!attached() && GetOpener())
479 GetOpener()->pending_new_windows_
.erase(this);
482 bool WebViewGuest::AddMessageToConsole(WebContents
* source
,
484 const base::string16
& message
,
486 const base::string16
& source_id
) {
487 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
488 // Log levels are from base/logging.h: LogSeverity.
489 args
->SetInteger(webview::kLevel
, level
);
490 args
->SetString(webview::kMessage
, message
);
491 args
->SetInteger(webview::kLine
, line_no
);
492 args
->SetString(webview::kSourceId
, source_id
);
494 new GuestViewEvent(webview::kEventConsoleMessage
, args
.Pass()));
498 void WebViewGuest::CloseContents(WebContents
* source
) {
499 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
501 new GuestViewEvent(webview::kEventClose
, args
.Pass()));
504 void WebViewGuest::FindReply(WebContents
* source
,
506 int number_of_matches
,
507 const gfx::Rect
& selection_rect
,
508 int active_match_ordinal
,
510 find_helper_
.FindReply(request_id
,
513 active_match_ordinal
,
517 double WebViewGuest::GetZoom() const {
519 ZoomController::FromWebContents(web_contents())->GetZoomLevel();
520 return ConvertZoomLevelToZoomFactor(zoom_level
);
523 ZoomController::ZoomMode
WebViewGuest::GetZoomMode() {
524 return ZoomController::FromWebContents(web_contents())->zoom_mode();
527 bool WebViewGuest::HandleContextMenu(
528 const content::ContextMenuParams
& params
) {
529 if (!web_view_guest_delegate_
)
531 return web_view_guest_delegate_
->HandleContextMenu(params
);
534 void WebViewGuest::HandleKeyboardEvent(
536 const content::NativeWebKeyboardEvent
& event
) {
537 if (HandleKeyboardShortcuts(event
))
540 GuestViewBase::HandleKeyboardEvent(source
, event
);
543 bool WebViewGuest::PreHandleGestureEvent(content::WebContents
* source
,
544 const blink::WebGestureEvent
& event
) {
545 return !allow_scaling_
&& GuestViewBase::PreHandleGestureEvent(source
, event
);
548 void WebViewGuest::LoadProgressChanged(content::WebContents
* source
,
550 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
551 args
->SetString(guest_view::kUrl
, web_contents()->GetURL().spec());
552 args
->SetDouble(webview::kProgress
, progress
);
554 new GuestViewEvent(webview::kEventLoadProgress
, args
.Pass()));
557 void WebViewGuest::LoadAbort(bool is_top_level
,
560 const std::string
& error_type
) {
561 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
562 args
->SetBoolean(guest_view::kIsTopLevel
, is_top_level
);
563 args
->SetString(guest_view::kUrl
, url
.possibly_invalid_spec());
564 args
->SetInteger(guest_view::kCode
, error_code
);
565 args
->SetString(guest_view::kReason
, error_type
);
567 new GuestViewEvent(webview::kEventLoadAbort
, args
.Pass()));
570 void WebViewGuest::CreateNewGuestWebViewWindow(
571 const content::OpenURLParams
& params
) {
572 GuestViewManager
* guest_manager
=
573 GuestViewManager::FromBrowserContext(browser_context());
574 // Set the attach params to use the same partition as the opener.
575 // We pull the partition information from the site's URL, which is of the
576 // form guest://site/{persist}?{partition_name}.
577 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
578 const std::string storage_partition_id
=
579 GetStoragePartitionIdFromSiteURL(site_url
);
580 base::DictionaryValue create_params
;
581 create_params
.SetString(webview::kStoragePartitionId
, storage_partition_id
);
583 guest_manager
->CreateGuest(WebViewGuest::Type
,
584 embedder_web_contents(),
586 base::Bind(&WebViewGuest::NewGuestWebViewCallback
,
587 weak_ptr_factory_
.GetWeakPtr(),
591 void WebViewGuest::NewGuestWebViewCallback(
592 const content::OpenURLParams
& params
,
593 content::WebContents
* guest_web_contents
) {
594 WebViewGuest
* new_guest
= WebViewGuest::FromWebContents(guest_web_contents
);
595 new_guest
->SetOpener(this);
597 // Take ownership of |new_guest|.
598 pending_new_windows_
.insert(
599 std::make_pair(new_guest
, NewWindowInfo(params
.url
, std::string())));
601 // Request permission to show the new window.
602 RequestNewWindowPermission(params
.disposition
,
605 new_guest
->web_contents());
608 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
609 // 'unresponsive' events.
610 void WebViewGuest::RendererResponsive(content::WebContents
* source
) {
611 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
612 args
->SetInteger(webview::kProcessId
,
613 web_contents()->GetRenderProcessHost()->GetID());
615 new GuestViewEvent(webview::kEventResponsive
, args
.Pass()));
618 void WebViewGuest::RendererUnresponsive(content::WebContents
* source
) {
619 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
620 args
->SetInteger(webview::kProcessId
,
621 web_contents()->GetRenderProcessHost()->GetID());
623 new GuestViewEvent(webview::kEventUnresponsive
, args
.Pass()));
626 void WebViewGuest::Observe(int type
,
627 const content::NotificationSource
& source
,
628 const content::NotificationDetails
& details
) {
630 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME
: {
631 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
632 if (content::Source
<WebContents
>(source
).ptr() == web_contents())
636 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
637 DCHECK_EQ(content::Source
<WebContents
>(source
).ptr(), web_contents());
638 content::ResourceRedirectDetails
* resource_redirect_details
=
639 content::Details
<content::ResourceRedirectDetails
>(details
).ptr();
640 bool is_top_level
= resource_redirect_details
->resource_type
==
641 content::RESOURCE_TYPE_MAIN_FRAME
;
642 LoadRedirect(resource_redirect_details
->url
,
643 resource_redirect_details
->new_url
,
648 NOTREACHED() << "Unexpected notification sent.";
653 void WebViewGuest::StartFindInternal(
654 const base::string16
& search_text
,
655 const blink::WebFindOptions
& options
,
656 scoped_refptr
<WebViewInternalFindFunction
> find_function
) {
657 find_helper_
.Find(web_contents(), search_text
, options
, find_function
);
660 void WebViewGuest::StopFindingInternal(content::StopFindAction action
) {
661 find_helper_
.CancelAllFindSessions();
662 web_contents()->StopFinding(action
);
665 bool WebViewGuest::Go(int relative_index
) {
666 content::NavigationController
& controller
= web_contents()->GetController();
667 if (!controller
.CanGoToOffset(relative_index
))
670 controller
.GoToOffset(relative_index
);
674 void WebViewGuest::Reload() {
675 // TODO(fsamuel): Don't check for repost because we don't want to show
676 // Chromium's repost warning. We might want to implement a separate API
677 // for registering a callback if a repost is about to happen.
678 web_contents()->GetController().Reload(false);
681 void WebViewGuest::SetUserAgentOverride(
682 const std::string
& user_agent_override
) {
683 is_overriding_user_agent_
= !user_agent_override
.empty();
684 if (is_overriding_user_agent_
) {
685 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
687 web_contents()->SetUserAgentOverride(user_agent_override
);
690 void WebViewGuest::Stop() {
691 web_contents()->Stop();
694 void WebViewGuest::Terminate() {
695 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
696 base::ProcessHandle process_handle
=
697 web_contents()->GetRenderProcessHost()->GetHandle();
699 web_contents()->GetRenderProcessHost()->Shutdown(
700 content::RESULT_CODE_KILLED
, false);
703 bool WebViewGuest::ClearData(base::Time remove_since
,
705 const base::Closure
& callback
) {
706 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
707 content::StoragePartition
* partition
=
708 content::BrowserContext::GetStoragePartition(
709 web_contents()->GetBrowserContext(),
710 web_contents()->GetSiteInstance());
715 if (removal_mask
& webview::WEB_VIEW_REMOVE_DATA_MASK_CACHE
) {
716 // First clear http cache data and then clear the rest in
717 // |ClearDataInternal|.
718 int render_process_id
= web_contents()->GetRenderProcessHost()->GetID();
719 // We need to clear renderer cache separately for our process because
720 // StoragePartitionHttpCacheDataRemover::ClearData() does not clear that.
721 web_cache::WebCacheManager::GetInstance()->Remove(render_process_id
);
722 web_cache::WebCacheManager::GetInstance()->ClearCacheForProcess(
725 base::Closure cache_removal_done_callback
= base::Bind(
726 &WebViewGuest::ClearDataInternal
, weak_ptr_factory_
.GetWeakPtr(),
727 remove_since
, removal_mask
, callback
);
728 // StoragePartitionHttpCacheDataRemover removes itself when it is done.
729 // components/, move |ClearCache| to WebViewGuest: http//crbug.com/471287.
730 browsing_data::StoragePartitionHttpCacheDataRemover::CreateForRange(
731 partition
, remove_since
, base::Time::Now())
732 ->Remove(cache_removal_done_callback
);
737 ClearDataInternal(remove_since
, removal_mask
, callback
);
741 WebViewGuest::WebViewGuest(content::WebContents
* owner_web_contents
)
742 : GuestView
<WebViewGuest
>(owner_web_contents
),
743 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID
),
745 is_overriding_user_agent_(false),
746 allow_transparency_(false),
747 javascript_dialog_helper_(this),
748 allow_scaling_(false),
749 is_guest_fullscreen_(false),
750 is_embedder_fullscreen_(false),
751 last_fullscreen_permission_was_allowed_by_embedder_(false),
752 pending_zoom_factor_(0.0),
753 weak_ptr_factory_(this) {
754 web_view_guest_delegate_
.reset(
755 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
758 WebViewGuest::~WebViewGuest() {
761 void WebViewGuest::DidCommitProvisionalLoadForFrame(
762 content::RenderFrameHost
* render_frame_host
,
764 ui::PageTransition transition_type
) {
765 if (!render_frame_host
->GetParent()) {
767 // Handle a pending zoom if one exists.
768 if (pending_zoom_factor_
) {
769 SetZoom(pending_zoom_factor_
);
770 pending_zoom_factor_
= 0.0;
773 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
774 args
->SetString(guest_view::kUrl
, url
.spec());
775 args
->SetBoolean(guest_view::kIsTopLevel
, !render_frame_host
->GetParent());
776 args
->SetString(webview::kInternalBaseURLForDataURL
,
779 .GetLastCommittedEntry()
780 ->GetBaseURLForDataURL()
782 args
->SetInteger(webview::kInternalCurrentEntryIndex
,
783 web_contents()->GetController().GetCurrentEntryIndex());
784 args
->SetInteger(webview::kInternalEntryCount
,
785 web_contents()->GetController().GetEntryCount());
786 args
->SetInteger(webview::kInternalProcessId
,
787 web_contents()->GetRenderProcessHost()->GetID());
789 new GuestViewEvent(webview::kEventLoadCommit
, args
.Pass()));
791 find_helper_
.CancelAllFindSessions();
794 void WebViewGuest::DidFailProvisionalLoad(
795 content::RenderFrameHost
* render_frame_host
,
796 const GURL
& validated_url
,
798 const base::string16
& error_description
,
799 bool was_ignored_by_handler
) {
800 // Suppress loadabort for "mailto" URLs.
801 if (validated_url
.SchemeIs(url::kMailToScheme
))
804 LoadAbort(!render_frame_host
->GetParent(), validated_url
, error_code
,
805 net::ErrorToShortString(error_code
));
808 void WebViewGuest::DidStartProvisionalLoadForFrame(
809 content::RenderFrameHost
* render_frame_host
,
810 const GURL
& validated_url
,
812 bool is_iframe_srcdoc
) {
813 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
814 args
->SetString(guest_view::kUrl
, validated_url
.spec());
815 args
->SetBoolean(guest_view::kIsTopLevel
, !render_frame_host
->GetParent());
817 new GuestViewEvent(webview::kEventLoadStart
, args
.Pass()));
820 void WebViewGuest::RenderProcessGone(base::TerminationStatus status
) {
821 // Cancel all find sessions in progress.
822 find_helper_
.CancelAllFindSessions();
824 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
825 args
->SetInteger(webview::kProcessId
,
826 web_contents()->GetRenderProcessHost()->GetID());
827 args
->SetString(webview::kReason
, TerminationStatusToString(status
));
829 new GuestViewEvent(webview::kEventExit
, args
.Pass()));
832 void WebViewGuest::UserAgentOverrideSet(const std::string
& user_agent
) {
833 content::NavigationController
& controller
= web_contents()->GetController();
834 content::NavigationEntry
* entry
= controller
.GetVisibleEntry();
837 entry
->SetIsOverridingUserAgent(!user_agent
.empty());
838 web_contents()->GetController().Reload(false);
841 void WebViewGuest::FrameNameChanged(RenderFrameHost
* render_frame_host
,
842 const std::string
& name
) {
843 if (render_frame_host
->GetParent())
849 ReportFrameNameChange(name
);
852 void WebViewGuest::ReportFrameNameChange(const std::string
& name
) {
854 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
855 args
->SetString(webview::kName
, name
);
857 new GuestViewEvent(webview::kEventFrameNameChanged
, args
.Pass()));
860 void WebViewGuest::LoadHandlerCalled() {
861 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
863 new GuestViewEvent(webview::kEventContentLoad
, args
.Pass()));
866 void WebViewGuest::LoadRedirect(const GURL
& old_url
,
869 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
870 args
->SetBoolean(guest_view::kIsTopLevel
, is_top_level
);
871 args
->SetString(webview::kNewURL
, new_url
.spec());
872 args
->SetString(webview::kOldURL
, old_url
.spec());
874 new GuestViewEvent(webview::kEventLoadRedirect
, args
.Pass()));
877 void WebViewGuest::PushWebViewStateToIOThread() {
878 const GURL
& site_url
= web_contents()->GetSiteInstance()->GetSiteURL();
879 std::string partition_domain
;
880 std::string partition_id
;
882 if (!GetGuestPartitionConfigForSite(
883 site_url
, &partition_domain
, &partition_id
, &in_memory
)) {
888 WebViewRendererState::WebViewInfo web_view_info
;
889 web_view_info
.embedder_process_id
=
890 owner_web_contents()->GetRenderProcessHost()->GetID();
891 web_view_info
.instance_id
= view_instance_id();
892 web_view_info
.partition_id
= partition_id
;
893 web_view_info
.owner_host
= owner_host();
894 web_view_info
.rules_registry_id
= rules_registry_id_
;
896 // Get content scripts IDs added by the guest.
897 WebViewContentScriptManager
* manager
=
898 WebViewContentScriptManager::Get(browser_context());
900 web_view_info
.content_script_ids
= manager
->GetContentScriptIDSet(
901 web_view_info
.embedder_process_id
, web_view_info
.instance_id
);
903 content::BrowserThread::PostTask(
904 content::BrowserThread::IO
,
906 base::Bind(&WebViewRendererState::AddGuest
,
907 base::Unretained(WebViewRendererState::GetInstance()),
908 web_contents()->GetRenderProcessHost()->GetID(),
909 web_contents()->GetRoutingID(),
914 void WebViewGuest::RemoveWebViewStateFromIOThread(
915 WebContents
* web_contents
) {
916 content::BrowserThread::PostTask(
917 content::BrowserThread::IO
, FROM_HERE
,
919 &WebViewRendererState::RemoveGuest
,
920 base::Unretained(WebViewRendererState::GetInstance()),
921 web_contents
->GetRenderProcessHost()->GetID(),
922 web_contents
->GetRoutingID()));
925 void WebViewGuest::RequestMediaAccessPermission(
926 content::WebContents
* source
,
927 const content::MediaStreamRequest
& request
,
928 const content::MediaResponseCallback
& callback
) {
929 web_view_permission_helper_
->RequestMediaAccessPermission(source
,
934 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents
* source
,
935 const GURL
& security_origin
,
936 content::MediaStreamType type
) {
937 return web_view_permission_helper_
->CheckMediaAccessPermission(
938 source
, security_origin
, type
);
941 void WebViewGuest::CanDownload(
943 const std::string
& request_method
,
944 const base::Callback
<void(bool)>& callback
) {
945 web_view_permission_helper_
->CanDownload(url
, request_method
, callback
);
948 void WebViewGuest::RequestPointerLockPermission(
950 bool last_unlocked_by_target
,
951 const base::Callback
<void(bool)>& callback
) {
952 web_view_permission_helper_
->RequestPointerLockPermission(
954 last_unlocked_by_target
,
958 void WebViewGuest::SignalWhenReady(const base::Closure
& callback
) {
959 auto manager
= WebViewContentScriptManager::Get(browser_context());
960 manager
->SignalOnScriptsLoaded(callback
);
963 void WebViewGuest::WillAttachToEmbedder() {
964 rules_registry_id_
= GetOrGenerateRulesRegistryID(
965 owner_web_contents()->GetRenderProcessHost()->GetID(),
968 // We must install the mapping from guests to WebViews prior to resuming
969 // suspended resource loads so that the WebRequest API will catch resource
971 PushWebViewStateToIOThread();
974 content::JavaScriptDialogManager
* WebViewGuest::GetJavaScriptDialogManager(
975 WebContents
* source
) {
976 return &javascript_dialog_helper_
;
979 void WebViewGuest::NavigateGuest(const std::string
& src
,
980 bool force_navigation
) {
984 GURL url
= ResolveURL(src
);
986 // We wait for all the content scripts to load and then navigate the guest
987 // if the navigation is embedder-initiated. For browser-initiated navigations,
988 // content scripts will be ready.
989 if (force_navigation
) {
990 SignalWhenReady(base::Bind(
991 &WebViewGuest::LoadURLWithParams
, weak_ptr_factory_
.GetWeakPtr(), url
,
992 content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
993 GlobalRequestID(), force_navigation
));
996 LoadURLWithParams(url
, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
997 GlobalRequestID(), force_navigation
);
1000 bool WebViewGuest::HandleKeyboardShortcuts(
1001 const content::NativeWebKeyboardEvent
& event
) {
1002 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
1003 if (!GuestViewManager::FromBrowserContext(browser_context())->
1004 IsOwnedByExtension(this)) {
1008 if (event
.type
!= blink::WebInputEvent::RawKeyDown
)
1011 // If the user hits the escape key without any modifiers then unlock the
1012 // mouse if necessary.
1013 if ((event
.windowsKeyCode
== ui::VKEY_ESCAPE
) &&
1014 !(event
.modifiers
& blink::WebInputEvent::InputModifiers
)) {
1015 return web_contents()->GotResponseToLockMouseRequest(false);
1018 #if defined(OS_MACOSX)
1019 if (event
.modifiers
!= blink::WebInputEvent::MetaKey
)
1022 if (event
.windowsKeyCode
== ui::VKEY_OEM_4
) {
1027 if (event
.windowsKeyCode
== ui::VKEY_OEM_6
) {
1032 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_BACK
) {
1037 if (event
.windowsKeyCode
== ui::VKEY_BROWSER_FORWARD
) {
1046 void WebViewGuest::ApplyAttributes(const base::DictionaryValue
& params
) {
1048 if (params
.GetString(webview::kAttributeName
, &name
)) {
1049 // If the guest window's name is empty, then the WebView tag's name is
1050 // assigned. Otherwise, the guest window's name takes precedence over the
1051 // WebView tag's name.
1056 ReportFrameNameChange(name_
);
1058 std::string user_agent_override
;
1059 params
.GetString(webview::kParameterUserAgentOverride
, &user_agent_override
);
1060 SetUserAgentOverride(user_agent_override
);
1062 bool allow_transparency
= false;
1063 if (params
.GetBoolean(webview::kAttributeAllowTransparency
,
1064 &allow_transparency
)) {
1065 // We need to set the background opaque flag after navigation to ensure that
1066 // there is a RenderWidgetHostView available.
1067 SetAllowTransparency(allow_transparency
);
1070 bool allow_scaling
= false;
1071 if (params
.GetBoolean(webview::kAttributeAllowScaling
, &allow_scaling
))
1072 SetAllowScaling(allow_scaling
);
1074 // Check for a pending zoom from before the first navigation.
1075 params
.GetDouble(webview::kInitialZoomFactor
, &pending_zoom_factor_
);
1077 bool is_pending_new_window
= false;
1079 // We need to do a navigation here if the target URL has changed between
1080 // the time the WebContents was created and the time it was attached.
1081 // We also need to do an initial navigation if a RenderView was never
1082 // created for the new window in cases where there is no referrer.
1083 auto it
= GetOpener()->pending_new_windows_
.find(this);
1084 if (it
!= GetOpener()->pending_new_windows_
.end()) {
1085 const NewWindowInfo
& new_window_info
= it
->second
;
1086 if (new_window_info
.changed
|| !web_contents()->HasOpener())
1087 NavigateGuest(new_window_info
.url
.spec(), false /* force_navigation */);
1089 // Once a new guest is attached to the DOM of the embedder page, then the
1090 // lifetime of the new guest is no longer managed by the opener guest.
1091 GetOpener()->pending_new_windows_
.erase(this);
1093 is_pending_new_window
= true;
1097 // Only read the src attribute if this is not a New Window API flow.
1098 if (!is_pending_new_window
) {
1100 if (params
.GetString(webview::kAttributeSrc
, &src
))
1101 NavigateGuest(src
, true /* force_navigation */);
1105 void WebViewGuest::ShowContextMenu(
1107 const WebViewGuestDelegate::MenuItemVector
* items
) {
1108 if (web_view_guest_delegate_
)
1109 web_view_guest_delegate_
->OnShowContextMenu(request_id
, items
);
1112 void WebViewGuest::SetName(const std::string
& name
) {
1117 Send(new ExtensionMsg_SetFrameName(routing_id(), name_
));
1120 void WebViewGuest::SetZoom(double zoom_factor
) {
1121 auto zoom_controller
= ZoomController::FromWebContents(web_contents());
1122 DCHECK(zoom_controller
);
1123 double zoom_level
= content::ZoomFactorToZoomLevel(zoom_factor
);
1124 zoom_controller
->SetZoomLevel(zoom_level
);
1127 void WebViewGuest::SetZoomMode(ZoomController::ZoomMode zoom_mode
) {
1128 ZoomController::FromWebContents(web_contents())->SetZoomMode(zoom_mode
);
1131 void WebViewGuest::SetAllowTransparency(bool allow
) {
1132 if (allow_transparency_
== allow
)
1135 allow_transparency_
= allow
;
1136 if (!web_contents()->GetRenderViewHost()->GetView())
1139 if (allow_transparency_
) {
1140 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1141 SK_ColorTRANSPARENT
);
1144 ->GetRenderViewHost()
1146 ->SetBackgroundColorToDefault();
1150 void WebViewGuest::SetAllowScaling(bool allow
) {
1151 allow_scaling_
= allow
;
1154 bool WebViewGuest::LoadDataWithBaseURL(const std::string
& data_url
,
1155 const std::string
& base_url
,
1156 const std::string
& virtual_url
,
1157 std::string
* error
) {
1158 // Make GURLs from URLs.
1159 const GURL data_gurl
= GURL(data_url
);
1160 const GURL base_gurl
= GURL(base_url
);
1161 const GURL virtual_gurl
= GURL(virtual_url
);
1163 // Check that the provided URLs are valid.
1164 // |data_url| must be a valid data URL.
1165 if (!data_gurl
.is_valid() || !data_gurl
.SchemeIs(url::kDataScheme
)) {
1166 base::SStringPrintf(
1167 error
, webview::kAPILoadDataInvalidDataURL
, data_url
.c_str());
1170 // |base_url| must be a valid URL.
1171 if (!base_gurl
.is_valid()) {
1172 base::SStringPrintf(
1173 error
, webview::kAPILoadDataInvalidBaseURL
, base_url
.c_str());
1176 // |virtual_url| must be a valid URL.
1177 if (!virtual_gurl
.is_valid()) {
1178 base::SStringPrintf(
1179 error
, webview::kAPILoadDataInvalidVirtualURL
, virtual_url
.c_str());
1183 // Set up the parameters to load |data_url| with the specified |base_url|.
1184 content::NavigationController::LoadURLParams
load_params(data_gurl
);
1185 load_params
.load_type
= content::NavigationController::LOAD_TYPE_DATA
;
1186 load_params
.base_url_for_data_url
= base_gurl
;
1187 load_params
.virtual_url_for_data_url
= virtual_gurl
;
1188 load_params
.override_user_agent
=
1189 content::NavigationController::UA_OVERRIDE_INHERIT
;
1191 // Navigate to the data URL.
1192 GuestViewBase::LoadURLWithParams(load_params
);
1197 void WebViewGuest::AddNewContents(content::WebContents
* source
,
1198 content::WebContents
* new_contents
,
1199 WindowOpenDisposition disposition
,
1200 const gfx::Rect
& initial_rect
,
1202 bool* was_blocked
) {
1204 *was_blocked
= false;
1205 RequestNewWindowPermission(disposition
,
1211 content::WebContents
* WebViewGuest::OpenURLFromTab(
1212 content::WebContents
* source
,
1213 const content::OpenURLParams
& params
) {
1214 // Most navigations should be handled by WebViewGuest::LoadURLWithParams,
1215 // which takes care of blocking chrome:// URLs and other web-unsafe schemes.
1216 // (NavigateGuest and CreateNewGuestWebViewWindow also go through
1217 // LoadURLWithParams.)
1219 // We make an exception here for context menu items, since the Language
1220 // Settings item uses a browser-initiated navigation to a chrome:// URL.
1221 // These can be passed to the embedder's WebContentsDelegate so that the
1222 // browser performs the action for the <webview>.
1223 if (!params
.is_renderer_initiated
&&
1224 !content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1225 params
.url
.scheme())) {
1226 if (!owner_web_contents()->GetDelegate())
1228 return owner_web_contents()->GetDelegate()->OpenURLFromTab(
1229 owner_web_contents(), params
);
1233 WebViewGuest
* opener
= GetOpener();
1234 // If the guest wishes to navigate away prior to attachment then we save the
1235 // navigation to perform upon attachment. Navigation initializes a lot of
1236 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1237 // Navigation also resumes resource loading. If we were created using
1238 // newwindow (i.e. we have an opener), we don't allow navigation until
1241 auto it
= opener
->pending_new_windows_
.find(this);
1242 if (it
== opener
->pending_new_windows_
.end())
1244 const NewWindowInfo
& info
= it
->second
;
1245 NewWindowInfo
new_window_info(params
.url
, info
.name
);
1246 new_window_info
.changed
= new_window_info
.url
!= info
.url
;
1247 it
->second
= new_window_info
;
1252 // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
1253 // decides that a fork should happen. At the time of writing this comment,
1254 // the only way a well behaving guest could hit this code path is if it
1255 // navigates to a URL that's associated with the default search engine.
1256 // This list of URLs is generated by search::GetSearchURLs. Validity checks
1257 // are performed inside LoadURLWithParams such that if the guest attempts
1258 // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
1259 // event will fire in the embedder, and the guest will be navigated to
1261 if (params
.disposition
== CURRENT_TAB
) {
1262 LoadURLWithParams(params
.url
, params
.referrer
, params
.transition
,
1263 params
.transferred_global_request_id
,
1264 true /* force_navigation */);
1265 return web_contents();
1268 // This code path is taken if Ctrl+Click, middle click or any of the
1269 // keyboard/mouse combinations are used to open a link in a new tab/window.
1270 // This code path is also taken on client-side redirects from about:blank.
1271 CreateNewGuestWebViewWindow(params
);
1275 void WebViewGuest::WebContentsCreated(WebContents
* source_contents
,
1276 int opener_render_frame_id
,
1277 const std::string
& frame_name
,
1278 const GURL
& target_url
,
1279 content::WebContents
* new_contents
) {
1280 auto guest
= WebViewGuest::FromWebContents(new_contents
);
1282 guest
->SetOpener(this);
1283 guest
->name_
= frame_name
;
1284 pending_new_windows_
.insert(
1285 std::make_pair(guest
, NewWindowInfo(target_url
, frame_name
)));
1288 void WebViewGuest::EnterFullscreenModeForTab(content::WebContents
* web_contents
,
1289 const GURL
& origin
) {
1290 // Ask the embedder for permission.
1291 base::DictionaryValue request_info
;
1292 request_info
.SetString(webview::kOrigin
, origin
.spec());
1293 web_view_permission_helper_
->RequestPermission(
1294 WEB_VIEW_PERMISSION_TYPE_FULLSCREEN
, request_info
,
1295 base::Bind(&WebViewGuest::OnFullscreenPermissionDecided
,
1296 weak_ptr_factory_
.GetWeakPtr()),
1297 false /* allowed_by_default */);
1299 // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
1300 // bounds. If the embedder denies the permission then we will see a flicker.
1301 // Once we have the ability to "cancel" a renderer/ fullscreen request:
1302 // http://crbug.com/466854 this won't be necessary and we should be
1303 // Calling SetFullscreenState(true) once the embedder allowed the request.
1304 // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
1305 SetFullscreenState(true);
1308 void WebViewGuest::ExitFullscreenModeForTab(
1309 content::WebContents
* web_contents
) {
1310 SetFullscreenState(false);
1313 bool WebViewGuest::IsFullscreenForTabOrPending(
1314 const content::WebContents
* web_contents
) const {
1315 return is_guest_fullscreen_
;
1318 void WebViewGuest::LoadURLWithParams(
1320 const content::Referrer
& referrer
,
1321 ui::PageTransition transition_type
,
1322 const GlobalRequestID
& transferred_global_request_id
,
1323 bool force_navigation
) {
1324 // Do not allow navigating a guest to schemes other than known safe schemes.
1325 // This will block the embedder trying to load unwanted schemes, e.g.
1327 bool scheme_is_blocked
=
1328 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1330 !url
.SchemeIs(url::kAboutScheme
)) ||
1331 url
.SchemeIs(url::kJavaScriptScheme
);
1332 if (scheme_is_blocked
|| !url
.is_valid()) {
1333 LoadAbort(true /* is_top_level */, url
, net::ERR_ABORTED
,
1334 net::ErrorToShortString(net::ERR_ABORTED
));
1335 NavigateGuest(url::kAboutBlankURL
, false /* force_navigation */);
1339 if (!force_navigation
&& (src_
== url
))
1342 GURL
validated_url(url
);
1343 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url
);
1344 // As guests do not swap processes on navigation, only navigations to
1345 // normal web URLs are supported. No protocol handlers are installed for
1346 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
1347 // can be granted to the guest process.
1348 content::NavigationController::LoadURLParams
load_url_params(validated_url
);
1349 load_url_params
.referrer
= referrer
;
1350 load_url_params
.transition_type
= transition_type
;
1351 load_url_params
.extra_headers
= std::string();
1352 load_url_params
.transferred_global_request_id
= transferred_global_request_id
;
1353 if (is_overriding_user_agent_
) {
1354 load_url_params
.override_user_agent
=
1355 content::NavigationController::UA_OVERRIDE_TRUE
;
1357 GuestViewBase::LoadURLWithParams(load_url_params
);
1359 src_
= validated_url
;
1362 void WebViewGuest::RequestNewWindowPermission(
1363 WindowOpenDisposition disposition
,
1364 const gfx::Rect
& initial_bounds
,
1366 content::WebContents
* new_contents
) {
1367 auto guest
= WebViewGuest::FromWebContents(new_contents
);
1370 auto it
= pending_new_windows_
.find(guest
);
1371 if (it
== pending_new_windows_
.end())
1373 const NewWindowInfo
& new_window_info
= it
->second
;
1375 // Retrieve the opener partition info if we have it.
1376 const GURL
& site_url
= new_contents
->GetSiteInstance()->GetSiteURL();
1377 std::string storage_partition_id
= GetStoragePartitionIdFromSiteURL(site_url
);
1379 base::DictionaryValue request_info
;
1380 request_info
.SetInteger(webview::kInitialHeight
, initial_bounds
.height());
1381 request_info
.SetInteger(webview::kInitialWidth
, initial_bounds
.width());
1382 request_info
.Set(webview::kTargetURL
,
1383 new base::StringValue(new_window_info
.url
.spec()));
1384 request_info
.Set(webview::kName
, new base::StringValue(new_window_info
.name
));
1385 request_info
.SetInteger(webview::kWindowID
, guest
->guest_instance_id());
1386 // We pass in partition info so that window-s created through newwindow
1387 // API can use it to set their partition attribute.
1388 request_info
.Set(webview::kStoragePartitionId
,
1389 new base::StringValue(storage_partition_id
));
1391 webview::kWindowOpenDisposition
,
1392 new base::StringValue(WindowOpenDispositionToString(disposition
)));
1394 web_view_permission_helper_
->
1395 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW
,
1397 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse
,
1398 weak_ptr_factory_
.GetWeakPtr(),
1399 guest
->guest_instance_id()),
1400 false /* allowed_by_default */);
1403 GURL
WebViewGuest::ResolveURL(const std::string
& src
) {
1404 if (!GuestViewManager::FromBrowserContext(browser_context())->
1405 IsOwnedByExtension(this)) {
1409 GURL
default_url(base::StringPrintf("%s://%s/",
1411 owner_host().c_str()));
1412 return default_url
.Resolve(src
);
1415 void WebViewGuest::OnWebViewNewWindowResponse(
1416 int new_window_instance_id
,
1418 const std::string
& user_input
) {
1420 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1421 new_window_instance_id
);
1429 void WebViewGuest::OnFullscreenPermissionDecided(
1431 const std::string
& user_input
) {
1432 last_fullscreen_permission_was_allowed_by_embedder_
= allowed
;
1433 SetFullscreenState(allowed
);
1436 bool WebViewGuest::GuestMadeEmbedderFullscreen() const {
1437 return last_fullscreen_permission_was_allowed_by_embedder_
&&
1438 is_embedder_fullscreen_
;
1441 void WebViewGuest::SetFullscreenState(bool is_fullscreen
) {
1442 if (is_fullscreen
== is_guest_fullscreen_
)
1445 bool was_fullscreen
= is_guest_fullscreen_
;
1446 is_guest_fullscreen_
= is_fullscreen
;
1447 // If the embedder entered fullscreen because of us, it should exit fullscreen
1448 // when we exit fullscreen.
1449 if (was_fullscreen
&& GuestMadeEmbedderFullscreen()) {
1450 // Dispatch a message so we can call document.webkitCancelFullscreen()
1452 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
1453 DispatchEventToView(
1454 new GuestViewEvent(webview::kEventExitFullscreen
, args
.Pass()));
1456 // Since we changed fullscreen state, sending a Resize message ensures that
1457 // renderer/ sees the change.
1458 web_contents()->GetRenderViewHost()->WasResized();
1461 } // namespace extensions