Remove unused parameter.
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob34c4a15d2bb9930e761965756b30f3f28c0458b7
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/guest_view/web_view/web_view_internal_api.h"
36 #include "extensions/browser/api/web_request/web_request_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::StoragePartition;
57 using content::WebContents;
58 using ui_zoom::ZoomController;
60 namespace extensions {
62 namespace {
64 // Returns storage partition removal mask from web_view clearData mask. Note
65 // that storage partition mask is a subset of webview's data removal mask.
66 uint32 GetStoragePartitionRemovalMask(uint32 web_view_removal_mask) {
67 uint32 mask = 0;
68 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_APPCACHE)
69 mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
70 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_COOKIES)
71 mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
72 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS)
73 mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
74 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB)
75 mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
76 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE)
77 mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
78 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL)
79 mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
81 return mask;
84 std::string WindowOpenDispositionToString(
85 WindowOpenDisposition window_open_disposition) {
86 switch (window_open_disposition) {
87 case IGNORE_ACTION:
88 return "ignore";
89 case SAVE_TO_DISK:
90 return "save_to_disk";
91 case CURRENT_TAB:
92 return "current_tab";
93 case NEW_BACKGROUND_TAB:
94 return "new_background_tab";
95 case NEW_FOREGROUND_TAB:
96 return "new_foreground_tab";
97 case NEW_WINDOW:
98 return "new_window";
99 case NEW_POPUP:
100 return "new_popup";
101 default:
102 NOTREACHED() << "Unknown Window Open Disposition";
103 return "ignore";
107 static std::string TerminationStatusToString(base::TerminationStatus status) {
108 switch (status) {
109 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
110 return "normal";
111 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
112 case base::TERMINATION_STATUS_STILL_RUNNING:
113 return "abnormal";
114 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
115 return "killed";
116 case base::TERMINATION_STATUS_PROCESS_CRASHED:
117 return "crashed";
118 case base::TERMINATION_STATUS_MAX_ENUM:
119 break;
121 NOTREACHED() << "Unknown Termination Status.";
122 return "unknown";
125 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
126 const std::string& partition_id = site_url.query();
127 bool persist_storage = site_url.path().find("persist") != std::string::npos;
128 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
131 void ParsePartitionParam(const base::DictionaryValue& create_params,
132 std::string* storage_partition_id,
133 bool* persist_storage) {
134 std::string partition_str;
135 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
136 return;
139 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
140 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
141 // remove the prefix without splicing in the middle of a multi-byte codepoint.
142 // We can use the rest of the string as UTF-8 encoded one.
143 if (StartsWithASCII(partition_str, "persist:", true)) {
144 size_t index = partition_str.find(":");
145 CHECK(index != std::string::npos);
146 // It is safe to do index + 1, since we tested for the full prefix above.
147 *storage_partition_id = partition_str.substr(index + 1);
149 if (storage_partition_id->empty()) {
150 // TODO(lazyboy): Better way to deal with this error.
151 return;
153 *persist_storage = true;
154 } else {
155 *storage_partition_id = partition_str;
156 *persist_storage = false;
160 void RemoveWebViewEventListenersOnIOThread(
161 void* profile,
162 const std::string& extension_id,
163 int embedder_process_id,
164 int view_instance_id) {
165 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
166 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
167 profile,
168 extension_id,
169 embedder_process_id,
170 view_instance_id);
173 double ConvertZoomLevelToZoomFactor(double zoom_level) {
174 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
175 // Because the conversion from zoom level to zoom factor isn't perfect, the
176 // resulting zoom factor is rounded to the nearest 6th decimal place.
177 zoom_factor = round(zoom_factor * 1000000) / 1000000;
178 return zoom_factor;
181 } // namespace
183 // static
184 GuestViewBase* WebViewGuest::Create(content::WebContents* owner_web_contents) {
185 return new WebViewGuest(owner_web_contents);
188 // static
189 bool WebViewGuest::GetGuestPartitionConfigForSite(
190 const GURL& site,
191 std::string* partition_domain,
192 std::string* partition_name,
193 bool* in_memory) {
194 if (!site.SchemeIs(content::kGuestScheme))
195 return false;
197 // Since guest URLs are only used for packaged apps, there must be an app
198 // id in the URL.
199 CHECK(site.has_host());
200 *partition_domain = site.host();
201 // Since persistence is optional, the path must either be empty or the
202 // literal string.
203 *in_memory = (site.path() != "/persist");
204 // The partition name is user supplied value, which we have encoded when the
205 // URL was created, so it needs to be decoded.
206 *partition_name =
207 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
208 return true;
211 // static
212 const char WebViewGuest::Type[] = "webview";
214 using WebViewKey = std::pair<int, int>;
215 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
216 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
217 LAZY_INSTANCE_INITIALIZER;
219 // static
220 int WebViewGuest::GetOrGenerateRulesRegistryID(
221 int embedder_process_id,
222 int webview_instance_id) {
223 bool is_web_view = embedder_process_id && webview_instance_id;
224 if (!is_web_view)
225 return RulesRegistryService::kDefaultRulesRegistryID;
227 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
228 auto it = web_view_key_to_id_map.Get().find(key);
229 if (it != web_view_key_to_id_map.Get().end())
230 return it->second;
232 auto rph = content::RenderProcessHost::FromID(embedder_process_id);
233 int rules_registry_id =
234 RulesRegistryService::Get(rph->GetBrowserContext())->
235 GetNextRulesRegistryID();
236 web_view_key_to_id_map.Get()[key] = rules_registry_id;
237 return rules_registry_id;
240 // static
241 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
242 auto guest = FromWebContents(contents);
243 if (!guest)
244 return guestview::kInstanceIDNone;
246 return guest->view_instance_id();
249 bool WebViewGuest::CanRunInDetachedState() const {
250 return true;
253 void WebViewGuest::CreateWebContents(
254 const base::DictionaryValue& create_params,
255 const WebContentsCreatedCallback& callback) {
256 content::RenderProcessHost* owner_render_process_host =
257 owner_web_contents()->GetRenderProcessHost();
258 std::string storage_partition_id;
259 bool persist_storage = false;
260 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
261 // Validate that the partition id coming from the renderer is valid UTF-8,
262 // since we depend on this in other parts of the code, such as FilePath
263 // creation. If the validation fails, treat it as a bad message and kill the
264 // renderer process.
265 if (!base::IsStringUTF8(storage_partition_id)) {
266 content::RecordAction(
267 base::UserMetricsAction("BadMessageTerminate_BPGM"));
268 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
269 false);
270 callback.Run(nullptr);
271 return;
273 std::string url_encoded_partition = net::EscapeQueryParamValue(
274 storage_partition_id, false);
275 std::string partition_domain = GetOwnerSiteURL().host();
276 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
277 content::kGuestScheme,
278 partition_domain.c_str(),
279 persist_storage ? "persist" : "",
280 url_encoded_partition.c_str()));
282 // If we already have a webview tag in the same app using the same storage
283 // partition, we should use the same SiteInstance so the existing tag and
284 // the new tag can script each other.
285 auto guest_view_manager = GuestViewManager::FromBrowserContext(
286 owner_render_process_host->GetBrowserContext());
287 content::SiteInstance* guest_site_instance =
288 guest_view_manager->GetGuestSiteInstance(guest_site);
289 if (!guest_site_instance) {
290 // Create the SiteInstance in a new BrowsingInstance, which will ensure
291 // that webview tags are also not allowed to send messages across
292 // different partitions.
293 guest_site_instance = content::SiteInstance::CreateForURL(
294 owner_render_process_host->GetBrowserContext(), guest_site);
296 WebContents::CreateParams params(
297 owner_render_process_host->GetBrowserContext(),
298 guest_site_instance);
299 params.guest_delegate = this;
300 callback.Run(WebContents::Create(params));
303 void WebViewGuest::DidAttachToEmbedder() {
304 ApplyAttributes(*attach_params());
307 void WebViewGuest::DidDropLink(const GURL& url) {
308 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
309 args->SetString(guestview::kUrl, url.spec());
310 DispatchEventToView(
311 new GuestViewBase::Event(webview::kEventDropLink, args.Pass()));
314 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
315 script_executor_.reset(
316 new ScriptExecutor(web_contents(), &script_observers_));
318 notification_registrar_.Add(this,
319 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
320 content::Source<WebContents>(web_contents()));
322 notification_registrar_.Add(this,
323 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
324 content::Source<WebContents>(web_contents()));
326 if (web_view_guest_delegate_)
327 web_view_guest_delegate_->OnDidInitialize();
328 AttachWebViewHelpers(web_contents());
330 rules_registry_id_ = GetOrGenerateRulesRegistryID(
331 owner_web_contents()->GetRenderProcessHost()->GetID(),
332 view_instance_id());
334 // We must install the mapping from guests to WebViews prior to resuming
335 // suspended resource loads so that the WebRequest API will catch resource
336 // requests.
337 PushWebViewStateToIOThread();
339 ApplyAttributes(create_params);
342 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
343 if (web_view_guest_delegate_)
344 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
345 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
348 void WebViewGuest::ClearDataInternal(base::Time remove_since,
349 uint32 removal_mask,
350 const base::Closure& callback) {
351 uint32 storage_partition_removal_mask =
352 GetStoragePartitionRemovalMask(removal_mask);
353 if (!storage_partition_removal_mask) {
354 callback.Run();
355 return;
357 content::StoragePartition* partition =
358 content::BrowserContext::GetStoragePartition(
359 web_contents()->GetBrowserContext(),
360 web_contents()->GetSiteInstance());
361 partition->ClearData(
362 storage_partition_removal_mask,
363 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, GURL(),
364 content::StoragePartition::OriginMatcherFunction(), remove_since,
365 base::Time::Now(), callback);
368 void WebViewGuest::GuestViewDidStopLoading() {
369 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
370 DispatchEventToView(
371 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
374 void WebViewGuest::EmbedderFullscreenToggled(bool entered_fullscreen) {
375 is_embedder_fullscreen_ = entered_fullscreen;
376 // If the embedder has got out of fullscreen, we get out of fullscreen
377 // mode as well.
378 if (!entered_fullscreen)
379 SetFullscreenState(false);
382 void WebViewGuest::EmbedderWillBeDestroyed() {
383 // Clean up rules registries for the webview.
384 RulesRegistryService::Get(browser_context())
385 ->RemoveRulesRegistriesByID(rules_registry_id_);
386 WebViewKey key(owner_web_contents()->GetRenderProcessHost()->GetID(),
387 view_instance_id());
388 web_view_key_to_id_map.Get().erase(key);
390 content::BrowserThread::PostTask(
391 content::BrowserThread::IO,
392 FROM_HERE,
393 base::Bind(
394 &RemoveWebViewEventListenersOnIOThread,
395 browser_context(),
396 owner_extension_id(),
397 owner_web_contents()->GetRenderProcessHost()->GetID(),
398 view_instance_id()));
401 const char* WebViewGuest::GetAPINamespace() const {
402 return webview::kAPINamespace;
405 int WebViewGuest::GetTaskPrefix() const {
406 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
409 void WebViewGuest::GuestDestroyed() {
410 // Clean up custom context menu items for this guest.
411 if (web_view_guest_delegate_)
412 web_view_guest_delegate_->OnGuestDestroyed();
413 RemoveWebViewStateFromIOThread(web_contents());
416 void WebViewGuest::GuestReady() {
417 // The guest RenderView should always live in an isolated guest process.
418 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
419 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
421 // We don't want to accidentally set the opacity of an interstitial page.
422 // WebContents::GetRenderWidgetHostView will return the RWHV of an
423 // interstitial page if one is showing at this time. We only want opacity
424 // to apply to web pages.
425 if (guest_opaque_) {
426 web_contents()
427 ->GetRenderViewHost()
428 ->GetView()
429 ->SetBackgroundColorToDefault();
430 } else {
431 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
432 SK_ColorTRANSPARENT);
436 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
437 const gfx::Size& new_size) {
438 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
439 args->SetInteger(webview::kOldHeight, old_size.height());
440 args->SetInteger(webview::kOldWidth, old_size.width());
441 args->SetInteger(webview::kNewHeight, new_size.height());
442 args->SetInteger(webview::kNewWidth, new_size.width());
443 DispatchEventToView(
444 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
447 bool WebViewGuest::IsAutoSizeSupported() const {
448 return true;
451 bool WebViewGuest::IsDragAndDropEnabled() const {
452 return true;
455 void WebViewGuest::GuestZoomChanged(double old_zoom_level,
456 double new_zoom_level) {
457 // Dispatch the zoomchange event.
458 double old_zoom_factor = ConvertZoomLevelToZoomFactor(old_zoom_level);
459 double new_zoom_factor = ConvertZoomLevelToZoomFactor(new_zoom_level);
460 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
461 args->SetDouble(webview::kOldZoomFactor, old_zoom_factor);
462 args->SetDouble(webview::kNewZoomFactor, new_zoom_factor);
463 DispatchEventToView(
464 new GuestViewBase::Event(webview::kEventZoomChange, args.Pass()));
467 void WebViewGuest::WillDestroy() {
468 if (!attached() && GetOpener())
469 GetOpener()->pending_new_windows_.erase(this);
472 bool WebViewGuest::AddMessageToConsole(WebContents* source,
473 int32 level,
474 const base::string16& message,
475 int32 line_no,
476 const base::string16& source_id) {
477 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
478 // Log levels are from base/logging.h: LogSeverity.
479 args->SetInteger(webview::kLevel, level);
480 args->SetString(webview::kMessage, message);
481 args->SetInteger(webview::kLine, line_no);
482 args->SetString(webview::kSourceId, source_id);
483 DispatchEventToView(
484 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
485 return true;
488 void WebViewGuest::CloseContents(WebContents* source) {
489 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
490 DispatchEventToView(
491 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
494 void WebViewGuest::FindReply(WebContents* source,
495 int request_id,
496 int number_of_matches,
497 const gfx::Rect& selection_rect,
498 int active_match_ordinal,
499 bool final_update) {
500 find_helper_.FindReply(request_id,
501 number_of_matches,
502 selection_rect,
503 active_match_ordinal,
504 final_update);
507 double WebViewGuest::GetZoom() const {
508 double zoom_level =
509 ZoomController::FromWebContents(web_contents())->GetZoomLevel();
510 return ConvertZoomLevelToZoomFactor(zoom_level);
513 ZoomController::ZoomMode WebViewGuest::GetZoomMode() {
514 return ZoomController::FromWebContents(web_contents())->zoom_mode();
517 bool WebViewGuest::HandleContextMenu(
518 const content::ContextMenuParams& params) {
519 if (!web_view_guest_delegate_)
520 return false;
521 return web_view_guest_delegate_->HandleContextMenu(params);
524 void WebViewGuest::HandleKeyboardEvent(
525 WebContents* source,
526 const content::NativeWebKeyboardEvent& event) {
527 if (HandleKeyboardShortcuts(event))
528 return;
530 GuestViewBase::HandleKeyboardEvent(source, event);
533 bool WebViewGuest::PreHandleGestureEvent(content::WebContents* source,
534 const blink::WebGestureEvent& event) {
535 return !allow_scaling_ && GuestViewBase::PreHandleGestureEvent(source, event);
538 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
539 double progress) {
540 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
541 args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
542 args->SetDouble(webview::kProgress, progress);
543 DispatchEventToView(
544 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
547 void WebViewGuest::LoadAbort(bool is_top_level,
548 const GURL& url,
549 const std::string& error_type) {
550 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
551 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
552 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
553 args->SetString(guestview::kReason, error_type);
554 DispatchEventToView(
555 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
558 void WebViewGuest::CreateNewGuestWebViewWindow(
559 const content::OpenURLParams& params) {
560 GuestViewManager* guest_manager =
561 GuestViewManager::FromBrowserContext(browser_context());
562 // Set the attach params to use the same partition as the opener.
563 // We pull the partition information from the site's URL, which is of the
564 // form guest://site/{persist}?{partition_name}.
565 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
566 const std::string storage_partition_id =
567 GetStoragePartitionIdFromSiteURL(site_url);
568 base::DictionaryValue create_params;
569 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
571 guest_manager->CreateGuest(WebViewGuest::Type,
572 embedder_web_contents(),
573 create_params,
574 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
575 weak_ptr_factory_.GetWeakPtr(),
576 params));
579 void WebViewGuest::NewGuestWebViewCallback(
580 const content::OpenURLParams& params,
581 content::WebContents* guest_web_contents) {
582 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
583 new_guest->SetOpener(this);
585 // Take ownership of |new_guest|.
586 pending_new_windows_.insert(
587 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
589 // Request permission to show the new window.
590 RequestNewWindowPermission(params.disposition,
591 gfx::Rect(),
592 params.user_gesture,
593 new_guest->web_contents());
596 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
597 // 'unresponsive' events.
598 void WebViewGuest::RendererResponsive(content::WebContents* source) {
599 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
600 args->SetInteger(webview::kProcessId,
601 web_contents()->GetRenderProcessHost()->GetID());
602 DispatchEventToView(
603 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
606 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
607 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
608 args->SetInteger(webview::kProcessId,
609 web_contents()->GetRenderProcessHost()->GetID());
610 DispatchEventToView(
611 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
614 void WebViewGuest::Observe(int type,
615 const content::NotificationSource& source,
616 const content::NotificationDetails& details) {
617 switch (type) {
618 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
619 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
620 if (content::Source<WebContents>(source).ptr() == web_contents())
621 LoadHandlerCalled();
622 break;
624 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
625 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
626 content::ResourceRedirectDetails* resource_redirect_details =
627 content::Details<content::ResourceRedirectDetails>(details).ptr();
628 bool is_top_level = resource_redirect_details->resource_type ==
629 content::RESOURCE_TYPE_MAIN_FRAME;
630 LoadRedirect(resource_redirect_details->url,
631 resource_redirect_details->new_url,
632 is_top_level);
633 break;
635 default:
636 NOTREACHED() << "Unexpected notification sent.";
637 break;
641 void WebViewGuest::StartFindInternal(
642 const base::string16& search_text,
643 const blink::WebFindOptions& options,
644 scoped_refptr<WebViewInternalFindFunction> find_function) {
645 find_helper_.Find(web_contents(), search_text, options, find_function);
648 void WebViewGuest::StopFindingInternal(content::StopFindAction action) {
649 find_helper_.CancelAllFindSessions();
650 web_contents()->StopFinding(action);
653 bool WebViewGuest::Go(int relative_index) {
654 content::NavigationController& controller = web_contents()->GetController();
655 if (!controller.CanGoToOffset(relative_index))
656 return false;
658 controller.GoToOffset(relative_index);
659 return true;
662 void WebViewGuest::Reload() {
663 // TODO(fsamuel): Don't check for repost because we don't want to show
664 // Chromium's repost warning. We might want to implement a separate API
665 // for registering a callback if a repost is about to happen.
666 web_contents()->GetController().Reload(false);
669 void WebViewGuest::SetUserAgentOverride(
670 const std::string& user_agent_override) {
671 is_overriding_user_agent_ = !user_agent_override.empty();
672 if (is_overriding_user_agent_) {
673 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
675 web_contents()->SetUserAgentOverride(user_agent_override);
678 void WebViewGuest::Stop() {
679 web_contents()->Stop();
682 void WebViewGuest::Terminate() {
683 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
684 base::ProcessHandle process_handle =
685 web_contents()->GetRenderProcessHost()->GetHandle();
686 if (process_handle)
687 web_contents()->GetRenderProcessHost()->Shutdown(
688 content::RESULT_CODE_KILLED, false);
691 bool WebViewGuest::ClearData(base::Time remove_since,
692 uint32 removal_mask,
693 const base::Closure& callback) {
694 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
695 content::StoragePartition* partition =
696 content::BrowserContext::GetStoragePartition(
697 web_contents()->GetBrowserContext(),
698 web_contents()->GetSiteInstance());
700 if (!partition)
701 return false;
703 if (removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_CACHE) {
704 if (web_view_guest_delegate_) {
705 // First clear http cache data and then clear the rest in
706 // |ClearDataInternal|.
707 web_view_guest_delegate_->ClearCache(
708 remove_since, base::Bind(&WebViewGuest::ClearDataInternal,
709 weak_ptr_factory_.GetWeakPtr(), remove_since,
710 removal_mask, callback));
711 return true;
715 ClearDataInternal(remove_since, removal_mask, callback);
716 return true;
719 WebViewGuest::WebViewGuest(content::WebContents* owner_web_contents)
720 : GuestView<WebViewGuest>(owner_web_contents),
721 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
722 find_helper_(this),
723 is_overriding_user_agent_(false),
724 guest_opaque_(true),
725 javascript_dialog_helper_(this),
726 allow_scaling_(false),
727 is_guest_fullscreen_(false),
728 is_embedder_fullscreen_(false),
729 last_fullscreen_permission_was_allowed_by_embedder_(false),
730 weak_ptr_factory_(this) {
731 web_view_guest_delegate_.reset(
732 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
735 WebViewGuest::~WebViewGuest() {
738 void WebViewGuest::DidCommitProvisionalLoadForFrame(
739 content::RenderFrameHost* render_frame_host,
740 const GURL& url,
741 ui::PageTransition transition_type) {
742 if (!render_frame_host->GetParent())
743 src_ = url;
744 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
745 args->SetString(guestview::kUrl, url.spec());
746 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
747 args->SetString(webview::kInternalBaseURLForDataURL,
748 web_contents()
749 ->GetController()
750 .GetLastCommittedEntry()
751 ->GetBaseURLForDataURL()
752 .spec());
753 args->SetInteger(webview::kInternalCurrentEntryIndex,
754 web_contents()->GetController().GetCurrentEntryIndex());
755 args->SetInteger(webview::kInternalEntryCount,
756 web_contents()->GetController().GetEntryCount());
757 args->SetInteger(webview::kInternalProcessId,
758 web_contents()->GetRenderProcessHost()->GetID());
759 DispatchEventToView(
760 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
762 find_helper_.CancelAllFindSessions();
764 if (web_view_guest_delegate_) {
765 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
766 !render_frame_host->GetParent());
770 void WebViewGuest::DidFailProvisionalLoad(
771 content::RenderFrameHost* render_frame_host,
772 const GURL& validated_url,
773 int error_code,
774 const base::string16& error_description) {
775 LoadAbort(!render_frame_host->GetParent(), validated_url,
776 net::ErrorToShortString(error_code));
779 void WebViewGuest::DidStartProvisionalLoadForFrame(
780 content::RenderFrameHost* render_frame_host,
781 const GURL& validated_url,
782 bool is_error_page,
783 bool is_iframe_srcdoc) {
784 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
785 args->SetString(guestview::kUrl, validated_url.spec());
786 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
787 DispatchEventToView(
788 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
791 void WebViewGuest::DocumentLoadedInFrame(
792 content::RenderFrameHost* render_frame_host) {
793 if (web_view_guest_delegate_)
794 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
797 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
798 // Cancel all find sessions in progress.
799 find_helper_.CancelAllFindSessions();
801 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
802 args->SetInteger(webview::kProcessId,
803 web_contents()->GetRenderProcessHost()->GetID());
804 args->SetString(webview::kReason, TerminationStatusToString(status));
805 DispatchEventToView(
806 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
809 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
810 content::NavigationController& controller = web_contents()->GetController();
811 content::NavigationEntry* entry = controller.GetVisibleEntry();
812 if (!entry)
813 return;
814 entry->SetIsOverridingUserAgent(!user_agent.empty());
815 web_contents()->GetController().Reload(false);
818 void WebViewGuest::FrameNameChanged(RenderFrameHost* render_frame_host,
819 const std::string& name) {
820 if (render_frame_host->GetParent())
821 return;
823 if (name_ == name)
824 return;
826 ReportFrameNameChange(name);
829 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
830 name_ = name;
831 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
832 args->SetString(webview::kName, name);
833 DispatchEventToView(
834 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
837 void WebViewGuest::LoadHandlerCalled() {
838 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
839 DispatchEventToView(
840 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass()));
843 void WebViewGuest::LoadRedirect(const GURL& old_url,
844 const GURL& new_url,
845 bool is_top_level) {
846 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
847 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
848 args->SetString(webview::kNewURL, new_url.spec());
849 args->SetString(webview::kOldURL, old_url.spec());
850 DispatchEventToView(
851 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass()));
854 void WebViewGuest::PushWebViewStateToIOThread() {
855 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
856 std::string partition_domain;
857 std::string partition_id;
858 bool in_memory;
859 if (!GetGuestPartitionConfigForSite(
860 site_url, &partition_domain, &partition_id, &in_memory)) {
861 NOTREACHED();
862 return;
865 WebViewRendererState::WebViewInfo web_view_info;
866 web_view_info.embedder_process_id =
867 owner_web_contents()->GetRenderProcessHost()->GetID();
868 web_view_info.instance_id = view_instance_id();
869 web_view_info.partition_id = partition_id;
870 web_view_info.owner_extension_id = owner_extension_id();
871 web_view_info.rules_registry_id = rules_registry_id_;
873 content::BrowserThread::PostTask(
874 content::BrowserThread::IO,
875 FROM_HERE,
876 base::Bind(&WebViewRendererState::AddGuest,
877 base::Unretained(WebViewRendererState::GetInstance()),
878 web_contents()->GetRenderProcessHost()->GetID(),
879 web_contents()->GetRoutingID(),
880 web_view_info));
883 // static
884 void WebViewGuest::RemoveWebViewStateFromIOThread(
885 WebContents* web_contents) {
886 content::BrowserThread::PostTask(
887 content::BrowserThread::IO, FROM_HERE,
888 base::Bind(
889 &WebViewRendererState::RemoveGuest,
890 base::Unretained(WebViewRendererState::GetInstance()),
891 web_contents->GetRenderProcessHost()->GetID(),
892 web_contents->GetRoutingID()));
895 void WebViewGuest::RequestMediaAccessPermission(
896 content::WebContents* source,
897 const content::MediaStreamRequest& request,
898 const content::MediaResponseCallback& callback) {
899 web_view_permission_helper_->RequestMediaAccessPermission(source,
900 request,
901 callback);
904 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
905 const GURL& security_origin,
906 content::MediaStreamType type) {
907 return web_view_permission_helper_->CheckMediaAccessPermission(
908 source, security_origin, type);
911 void WebViewGuest::CanDownload(
912 content::RenderViewHost* render_view_host,
913 const GURL& url,
914 const std::string& request_method,
915 const base::Callback<void(bool)>& callback) {
916 web_view_permission_helper_->CanDownload(render_view_host,
917 url,
918 request_method,
919 callback);
922 void WebViewGuest::RequestPointerLockPermission(
923 bool user_gesture,
924 bool last_unlocked_by_target,
925 const base::Callback<void(bool)>& callback) {
926 web_view_permission_helper_->RequestPointerLockPermission(
927 user_gesture,
928 last_unlocked_by_target,
929 callback);
932 void WebViewGuest::WillAttachToEmbedder() {
933 rules_registry_id_ = GetOrGenerateRulesRegistryID(
934 owner_web_contents()->GetRenderProcessHost()->GetID(),
935 view_instance_id());
937 // We must install the mapping from guests to WebViews prior to resuming
938 // suspended resource loads so that the WebRequest API will catch resource
939 // requests.
940 PushWebViewStateToIOThread();
943 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
944 WebContents* source) {
945 return &javascript_dialog_helper_;
948 void WebViewGuest::NavigateGuest(const std::string& src,
949 bool force_navigation) {
950 if (src.empty())
951 return;
953 GURL url = ResolveURL(src);
955 LoadURLWithParams(url, content::Referrer(),
956 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
957 force_navigation);
960 bool WebViewGuest::HandleKeyboardShortcuts(
961 const content::NativeWebKeyboardEvent& event) {
962 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
963 if (!in_extension())
964 return false;
966 if (event.type != blink::WebInputEvent::RawKeyDown)
967 return false;
969 // If the user hits the escape key without any modifiers then unlock the
970 // mouse if necessary.
971 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
972 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
973 return web_contents()->GotResponseToLockMouseRequest(false);
976 #if defined(OS_MACOSX)
977 if (event.modifiers != blink::WebInputEvent::MetaKey)
978 return false;
980 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
981 Go(-1);
982 return true;
985 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
986 Go(1);
987 return true;
989 #else
990 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
991 Go(-1);
992 return true;
995 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
996 Go(1);
997 return true;
999 #endif
1001 return false;
1004 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
1005 std::string name;
1006 if (params.GetString(webview::kAttributeName, &name)) {
1007 // If the guest window's name is empty, then the WebView tag's name is
1008 // assigned. Otherwise, the guest window's name takes precedence over the
1009 // WebView tag's name.
1010 if (name_.empty())
1011 SetName(name);
1013 if (attached())
1014 ReportFrameNameChange(name_);
1016 std::string user_agent_override;
1017 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
1018 SetUserAgentOverride(user_agent_override);
1020 bool allow_transparency = false;
1021 params.GetBoolean(webview::kAttributeAllowTransparency, &allow_transparency);
1022 // We need to set the background opaque flag after navigation to ensure that
1023 // there is a RenderWidgetHostView available.
1024 SetAllowTransparency(allow_transparency);
1026 bool allow_scaling = false;
1027 params.GetBoolean(webview::kAttributeAllowScaling, &allow_scaling);
1028 SetAllowScaling(allow_scaling);
1030 bool is_pending_new_window = false;
1031 if (GetOpener()) {
1032 // We need to do a navigation here if the target URL has changed between
1033 // the time the WebContents was created and the time it was attached.
1034 // We also need to do an initial navigation if a RenderView was never
1035 // created for the new window in cases where there is no referrer.
1036 auto it = GetOpener()->pending_new_windows_.find(this);
1037 if (it != GetOpener()->pending_new_windows_.end()) {
1038 const NewWindowInfo& new_window_info = it->second;
1039 if (new_window_info.changed || !web_contents()->HasOpener())
1040 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
1042 // Once a new guest is attached to the DOM of the embedder page, then the
1043 // lifetime of the new guest is no longer managed by the opener guest.
1044 GetOpener()->pending_new_windows_.erase(this);
1046 is_pending_new_window = true;
1050 // Only read the src attribute if this is not a New Window API flow.
1051 if (!is_pending_new_window) {
1052 std::string src;
1053 params.GetString(webview::kAttributeSrc, &src);
1054 NavigateGuest(src, false /* force_navigation */);
1058 void WebViewGuest::ShowContextMenu(
1059 int request_id,
1060 const WebViewGuestDelegate::MenuItemVector* items) {
1061 if (web_view_guest_delegate_)
1062 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1065 void WebViewGuest::SetName(const std::string& name) {
1066 if (name_ == name)
1067 return;
1068 name_ = name;
1070 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1073 void WebViewGuest::SetZoom(double zoom_factor) {
1074 auto zoom_controller = ZoomController::FromWebContents(web_contents());
1075 DCHECK(zoom_controller);
1076 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1077 zoom_controller->SetZoomLevel(zoom_level);
1080 void WebViewGuest::SetZoomMode(ZoomController::ZoomMode zoom_mode) {
1081 ZoomController::FromWebContents(web_contents())->SetZoomMode(zoom_mode);
1084 void WebViewGuest::SetAllowTransparency(bool allow) {
1085 if (guest_opaque_ != allow)
1086 return;
1088 guest_opaque_ = !allow;
1089 if (!web_contents()->GetRenderViewHost()->GetView())
1090 return;
1092 if (guest_opaque_) {
1093 web_contents()
1094 ->GetRenderViewHost()
1095 ->GetView()
1096 ->SetBackgroundColorToDefault();
1097 } else {
1098 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1099 SK_ColorTRANSPARENT);
1103 void WebViewGuest::SetAllowScaling(bool allow) {
1104 allow_scaling_ = allow;
1107 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1108 const std::string& base_url,
1109 const std::string& virtual_url,
1110 std::string* error) {
1111 // Make GURLs from URLs.
1112 const GURL data_gurl = GURL(data_url);
1113 const GURL base_gurl = GURL(base_url);
1114 const GURL virtual_gurl = GURL(virtual_url);
1116 // Check that the provided URLs are valid.
1117 // |data_url| must be a valid data URL.
1118 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1119 base::SStringPrintf(
1120 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1121 return false;
1123 // |base_url| must be a valid URL.
1124 if (!base_gurl.is_valid()) {
1125 base::SStringPrintf(
1126 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1127 return false;
1129 // |virtual_url| must be a valid URL.
1130 if (!virtual_gurl.is_valid()) {
1131 base::SStringPrintf(
1132 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1133 return false;
1136 // Set up the parameters to load |data_url| with the specified |base_url|.
1137 content::NavigationController::LoadURLParams load_params(data_gurl);
1138 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1139 load_params.base_url_for_data_url = base_gurl;
1140 load_params.virtual_url_for_data_url = virtual_gurl;
1141 load_params.override_user_agent =
1142 content::NavigationController::UA_OVERRIDE_INHERIT;
1144 // Navigate to the data URL.
1145 GuestViewBase::LoadURLWithParams(load_params);
1147 return true;
1150 void WebViewGuest::AddNewContents(content::WebContents* source,
1151 content::WebContents* new_contents,
1152 WindowOpenDisposition disposition,
1153 const gfx::Rect& initial_rect,
1154 bool user_gesture,
1155 bool* was_blocked) {
1156 if (was_blocked)
1157 *was_blocked = false;
1158 RequestNewWindowPermission(disposition,
1159 initial_rect,
1160 user_gesture,
1161 new_contents);
1164 content::WebContents* WebViewGuest::OpenURLFromTab(
1165 content::WebContents* source,
1166 const content::OpenURLParams& params) {
1167 // There are two use cases to consider from a security perspective:
1168 // 1.) Renderer-initiated navigation to chrome:// must always be blocked even
1169 // if the <webview> is in WebUI. This is handled by
1170 // WebViewGuest::LoadURLWithParams. WebViewGuest::NavigateGuest will also
1171 // call LoadURLWithParams. CreateNewGuestWebViewWindow creates a new
1172 // WebViewGuest which will call NavigateGuest in DidInitialize.
1173 // 2.) The Language Settings context menu item should always work, both in
1174 // Chrome Apps and WebUI. This is a browser initiated request and so
1175 // we pass it along to the embedder's WebContentsDelegate to get the
1176 // browser to perform the action for the <webview>.
1177 if (!params.is_renderer_initiated) {
1178 if (!owner_web_contents()->GetDelegate())
1179 return nullptr;
1180 return owner_web_contents()->GetDelegate()->OpenURLFromTab(
1181 owner_web_contents(), params);
1184 // If the guest wishes to navigate away prior to attachment then we save the
1185 // navigation to perform upon attachment. Navigation initializes a lot of
1186 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1187 // Navigation also resumes resource loading which we don't want to allow
1188 // until attachment.
1189 if (!attached()) {
1190 WebViewGuest* opener = GetOpener();
1191 auto it = opener->pending_new_windows_.find(this);
1192 if (it == opener->pending_new_windows_.end())
1193 return nullptr;
1194 const NewWindowInfo& info = it->second;
1195 NewWindowInfo new_window_info(params.url, info.name);
1196 new_window_info.changed = new_window_info.url != info.url;
1197 it->second = new_window_info;
1198 return nullptr;
1201 // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
1202 // decides that a fork should happen. At the time of writing this comment,
1203 // the only way a well behaving guest could hit this code path is if it
1204 // navigates to a URL that's associated with the default search engine.
1205 // This list of URLs is generated by chrome::GetSearchURLs. Validity checks
1206 // are performed inside LoadURLWithParams such that if the guest attempts
1207 // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
1208 // event will fire in the embedder, and the guest will be navigated to
1209 // about:blank.
1210 if (params.disposition == CURRENT_TAB) {
1211 LoadURLWithParams(params.url, params.referrer, params.transition,
1212 true /* force_navigation */);
1213 return web_contents();
1216 // This code path is taken if Ctrl+Click, middle click or any of the
1217 // keyboard/mouse combinations are used to open a link in a new tab/window.
1218 // This code path is also taken on client-side redirects from about:blank.
1219 CreateNewGuestWebViewWindow(params);
1220 return nullptr;
1223 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1224 int opener_render_frame_id,
1225 const base::string16& frame_name,
1226 const GURL& target_url,
1227 content::WebContents* new_contents) {
1228 auto guest = WebViewGuest::FromWebContents(new_contents);
1229 CHECK(guest);
1230 guest->SetOpener(this);
1231 std::string guest_name = base::UTF16ToUTF8(frame_name);
1232 guest->name_ = guest_name;
1233 pending_new_windows_.insert(
1234 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1237 void WebViewGuest::EnterFullscreenModeForTab(content::WebContents* web_contents,
1238 const GURL& origin) {
1239 // Ask the embedder for permission.
1240 base::DictionaryValue request_info;
1241 request_info.SetString(webview::kOrigin, origin.spec());
1242 web_view_permission_helper_->RequestPermission(
1243 WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, request_info,
1244 base::Bind(&WebViewGuest::OnFullscreenPermissionDecided,
1245 weak_ptr_factory_.GetWeakPtr()),
1246 false /* allowed_by_default */);
1248 // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
1249 // bounds. If the embedder denies the permission then we will see a flicker.
1250 // Once we have the ability to "cancel" a renderer/ fullscreen request:
1251 // http://crbug.com/466854 this won't be necessary and we should be
1252 // Calling SetFullscreenState(true) once the embedder allowed the request.
1253 // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
1254 SetFullscreenState(true);
1257 void WebViewGuest::ExitFullscreenModeForTab(
1258 content::WebContents* web_contents) {
1259 SetFullscreenState(false);
1262 bool WebViewGuest::IsFullscreenForTabOrPending(
1263 const content::WebContents* web_contents) const {
1264 return is_guest_fullscreen_;
1267 void WebViewGuest::LoadURLWithParams(const GURL& url,
1268 const content::Referrer& referrer,
1269 ui::PageTransition transition_type,
1270 bool force_navigation) {
1271 // Do not allow navigating a guest to schemes other than known safe schemes.
1272 // This will block the embedder trying to load unwanted schemes, e.g.
1273 // chrome://.
1274 bool scheme_is_blocked =
1275 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1276 url.scheme()) &&
1277 !url.SchemeIs(url::kAboutScheme)) ||
1278 url.SchemeIs(url::kJavaScriptScheme);
1279 if (scheme_is_blocked || !url.is_valid()) {
1280 LoadAbort(true /* is_top_level */, url,
1281 net::ErrorToShortString(net::ERR_ABORTED));
1282 NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
1283 return;
1286 if (!force_navigation && (src_ == url))
1287 return;
1289 GURL validated_url(url);
1290 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
1291 // As guests do not swap processes on navigation, only navigations to
1292 // normal web URLs are supported. No protocol handlers are installed for
1293 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
1294 // can be granted to the guest process.
1295 content::NavigationController::LoadURLParams load_url_params(validated_url);
1296 load_url_params.referrer = referrer;
1297 load_url_params.transition_type = transition_type;
1298 load_url_params.extra_headers = std::string();
1299 if (is_overriding_user_agent_) {
1300 load_url_params.override_user_agent =
1301 content::NavigationController::UA_OVERRIDE_TRUE;
1303 GuestViewBase::LoadURLWithParams(load_url_params);
1305 src_ = validated_url;
1308 void WebViewGuest::RequestNewWindowPermission(
1309 WindowOpenDisposition disposition,
1310 const gfx::Rect& initial_bounds,
1311 bool user_gesture,
1312 content::WebContents* new_contents) {
1313 auto guest = WebViewGuest::FromWebContents(new_contents);
1314 if (!guest)
1315 return;
1316 auto it = pending_new_windows_.find(guest);
1317 if (it == pending_new_windows_.end())
1318 return;
1319 const NewWindowInfo& new_window_info = it->second;
1321 // Retrieve the opener partition info if we have it.
1322 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1323 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1325 base::DictionaryValue request_info;
1326 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1327 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1328 request_info.Set(webview::kTargetURL,
1329 new base::StringValue(new_window_info.url.spec()));
1330 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1331 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1332 // We pass in partition info so that window-s created through newwindow
1333 // API can use it to set their partition attribute.
1334 request_info.Set(webview::kStoragePartitionId,
1335 new base::StringValue(storage_partition_id));
1336 request_info.Set(
1337 webview::kWindowOpenDisposition,
1338 new base::StringValue(WindowOpenDispositionToString(disposition)));
1340 web_view_permission_helper_->
1341 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1342 request_info,
1343 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1344 weak_ptr_factory_.GetWeakPtr(),
1345 guest->guest_instance_id()),
1346 false /* allowed_by_default */);
1349 GURL WebViewGuest::ResolveURL(const std::string& src) {
1350 if (!in_extension())
1351 return GURL(src);
1353 GURL default_url(base::StringPrintf("%s://%s/",
1354 kExtensionScheme,
1355 owner_extension_id().c_str()));
1356 return default_url.Resolve(src);
1359 void WebViewGuest::OnWebViewNewWindowResponse(
1360 int new_window_instance_id,
1361 bool allow,
1362 const std::string& user_input) {
1363 auto guest =
1364 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1365 new_window_instance_id);
1366 if (!guest)
1367 return;
1369 if (!allow)
1370 guest->Destroy();
1373 void WebViewGuest::OnFullscreenPermissionDecided(
1374 bool allowed,
1375 const std::string& user_input) {
1376 last_fullscreen_permission_was_allowed_by_embedder_ = allowed;
1377 SetFullscreenState(allowed);
1380 bool WebViewGuest::GuestMadeEmbedderFullscreen() const {
1381 return last_fullscreen_permission_was_allowed_by_embedder_ &&
1382 is_embedder_fullscreen_;
1385 void WebViewGuest::SetFullscreenState(bool is_fullscreen) {
1386 if (is_fullscreen == is_guest_fullscreen_)
1387 return;
1389 bool was_fullscreen = is_guest_fullscreen_;
1390 is_guest_fullscreen_ = is_fullscreen;
1391 // If the embedder entered fullscreen because of us, it should exit fullscreen
1392 // when we exit fullscreen.
1393 if (was_fullscreen && GuestMadeEmbedderFullscreen()) {
1394 // Dispatch a message so we can call document.webkitCancelFullscreen()
1395 // on the embedder.
1396 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1397 DispatchEventToView(
1398 new GuestViewBase::Event(webview::kEventExitFullscreen, args.Pass()));
1400 // Since we changed fullscreen state, sending a Resize message ensures that
1401 // renderer/ sees the change.
1402 web_contents()->GetRenderViewHost()->WasResized();
1405 } // namespace extensions