Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob7a5f4f2dada3c527fbafb8038960f730d785daed
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/guest_view/web_view/web_view_constants.h"
44 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
45 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
46 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
47 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
48 #include "extensions/common/constants.h"
49 #include "extensions/common/extension_messages.h"
50 #include "extensions/strings/grit/extensions_strings.h"
51 #include "ipc/ipc_message_macros.h"
52 #include "net/base/escape.h"
53 #include "net/base/net_errors.h"
54 #include "ui/base/models/simple_menu_model.h"
55 #include "url/url_constants.h"
57 using base::UserMetricsAction;
58 using content::RenderFrameHost;
59 using content::ResourceType;
60 using content::StoragePartition;
61 using content::WebContents;
62 using guest_view::GuestViewBase;
63 using guest_view::GuestViewEvent;
64 using guest_view::GuestViewManager;
65 using ui_zoom::ZoomController;
67 namespace extensions {
69 namespace {
71 // Returns storage partition removal mask from web_view clearData mask. Note
72 // that storage partition mask is a subset of webview's data removal mask.
73 uint32 GetStoragePartitionRemovalMask(uint32 web_view_removal_mask) {
74 uint32 mask = 0;
75 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_APPCACHE)
76 mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
77 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_COOKIES)
78 mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
79 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS)
80 mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
81 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB)
82 mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
83 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE)
84 mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
85 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL)
86 mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
88 return mask;
91 std::string WindowOpenDispositionToString(
92 WindowOpenDisposition window_open_disposition) {
93 switch (window_open_disposition) {
94 case IGNORE_ACTION:
95 return "ignore";
96 case SAVE_TO_DISK:
97 return "save_to_disk";
98 case CURRENT_TAB:
99 return "current_tab";
100 case NEW_BACKGROUND_TAB:
101 return "new_background_tab";
102 case NEW_FOREGROUND_TAB:
103 return "new_foreground_tab";
104 case NEW_WINDOW:
105 return "new_window";
106 case NEW_POPUP:
107 return "new_popup";
108 default:
109 NOTREACHED() << "Unknown Window Open Disposition";
110 return "ignore";
114 static std::string TerminationStatusToString(base::TerminationStatus status) {
115 switch (status) {
116 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
117 return "normal";
118 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
119 case base::TERMINATION_STATUS_STILL_RUNNING:
120 return "abnormal";
121 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
122 return "killed";
123 case base::TERMINATION_STATUS_PROCESS_CRASHED:
124 return "crashed";
125 case base::TERMINATION_STATUS_MAX_ENUM:
126 break;
128 NOTREACHED() << "Unknown Termination Status.";
129 return "unknown";
132 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
133 const std::string& partition_id = site_url.query();
134 bool persist_storage = site_url.path().find("persist") != std::string::npos;
135 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
138 void ParsePartitionParam(const base::DictionaryValue& create_params,
139 std::string* storage_partition_id,
140 bool* persist_storage) {
141 std::string partition_str;
142 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
143 return;
146 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
147 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
148 // remove the prefix without splicing in the middle of a multi-byte codepoint.
149 // We can use the rest of the string as UTF-8 encoded one.
150 if (StartsWithASCII(partition_str, "persist:", true)) {
151 size_t index = partition_str.find(":");
152 CHECK(index != std::string::npos);
153 // It is safe to do index + 1, since we tested for the full prefix above.
154 *storage_partition_id = partition_str.substr(index + 1);
156 if (storage_partition_id->empty()) {
157 // TODO(lazyboy): Better way to deal with this error.
158 return;
160 *persist_storage = true;
161 } else {
162 *storage_partition_id = partition_str;
163 *persist_storage = false;
167 void RemoveWebViewEventListenersOnIOThread(
168 void* profile,
169 const std::string& extension_id,
170 int embedder_process_id,
171 int view_instance_id) {
172 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
173 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
174 profile,
175 extension_id,
176 embedder_process_id,
177 view_instance_id);
180 double ConvertZoomLevelToZoomFactor(double zoom_level) {
181 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
182 // Because the conversion from zoom level to zoom factor isn't perfect, the
183 // resulting zoom factor is rounded to the nearest 6th decimal place.
184 zoom_factor = round(zoom_factor * 1000000) / 1000000;
185 return zoom_factor;
188 } // namespace
190 // static
191 GuestViewBase* WebViewGuest::Create(content::WebContents* owner_web_contents) {
192 return new WebViewGuest(owner_web_contents);
195 // static
196 bool WebViewGuest::GetGuestPartitionConfigForSite(
197 const GURL& site,
198 std::string* partition_domain,
199 std::string* partition_name,
200 bool* in_memory) {
201 if (!site.SchemeIs(content::kGuestScheme))
202 return false;
204 // Since guest URLs are only used for packaged apps, there must be an app
205 // id in the URL.
206 CHECK(site.has_host());
207 *partition_domain = site.host();
208 // Since persistence is optional, the path must either be empty or the
209 // literal string.
210 *in_memory = (site.path() != "/persist");
211 // The partition name is user supplied value, which we have encoded when the
212 // URL was created, so it needs to be decoded.
213 *partition_name =
214 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
215 return true;
218 // static
219 const char WebViewGuest::Type[] = "webview";
221 using WebViewKey = std::pair<int, int>;
222 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
223 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
224 LAZY_INSTANCE_INITIALIZER;
226 // static
227 int WebViewGuest::GetOrGenerateRulesRegistryID(
228 int embedder_process_id,
229 int webview_instance_id) {
230 bool is_web_view = embedder_process_id && webview_instance_id;
231 if (!is_web_view)
232 return RulesRegistryService::kDefaultRulesRegistryID;
234 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
235 auto it = web_view_key_to_id_map.Get().find(key);
236 if (it != web_view_key_to_id_map.Get().end())
237 return it->second;
239 auto rph = content::RenderProcessHost::FromID(embedder_process_id);
240 int rules_registry_id =
241 RulesRegistryService::Get(rph->GetBrowserContext())->
242 GetNextRulesRegistryID();
243 web_view_key_to_id_map.Get()[key] = rules_registry_id;
244 return rules_registry_id;
247 // static
248 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
249 auto guest = FromWebContents(contents);
250 if (!guest)
251 return guest_view::kInstanceIDNone;
253 return guest->view_instance_id();
256 bool WebViewGuest::CanRunInDetachedState() const {
257 return true;
260 void WebViewGuest::CreateWebContents(
261 const base::DictionaryValue& create_params,
262 const WebContentsCreatedCallback& callback) {
263 content::RenderProcessHost* owner_render_process_host =
264 owner_web_contents()->GetRenderProcessHost();
265 std::string storage_partition_id;
266 bool persist_storage = false;
267 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
268 // Validate that the partition id coming from the renderer is valid UTF-8,
269 // since we depend on this in other parts of the code, such as FilePath
270 // creation. If the validation fails, treat it as a bad message and kill the
271 // renderer process.
272 if (!base::IsStringUTF8(storage_partition_id)) {
273 content::RecordAction(
274 base::UserMetricsAction("BadMessageTerminate_BPGM"));
275 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
276 false);
277 callback.Run(nullptr);
278 return;
280 std::string url_encoded_partition = net::EscapeQueryParamValue(
281 storage_partition_id, false);
282 std::string partition_domain = GetOwnerSiteURL().host();
283 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
284 content::kGuestScheme,
285 partition_domain.c_str(),
286 persist_storage ? "persist" : "",
287 url_encoded_partition.c_str()));
289 // If we already have a webview tag in the same app using the same storage
290 // partition, we should use the same SiteInstance so the existing tag and
291 // the new tag can script each other.
292 auto guest_view_manager = GuestViewManager::FromBrowserContext(
293 owner_render_process_host->GetBrowserContext());
294 content::SiteInstance* guest_site_instance =
295 guest_view_manager->GetGuestSiteInstance(guest_site);
296 if (!guest_site_instance) {
297 // Create the SiteInstance in a new BrowsingInstance, which will ensure
298 // that webview tags are also not allowed to send messages across
299 // different partitions.
300 guest_site_instance = content::SiteInstance::CreateForURL(
301 owner_render_process_host->GetBrowserContext(), guest_site);
303 WebContents::CreateParams params(
304 owner_render_process_host->GetBrowserContext(),
305 guest_site_instance);
306 params.guest_delegate = this;
307 callback.Run(WebContents::Create(params));
310 void WebViewGuest::DidAttachToEmbedder() {
311 ApplyAttributes(*attach_params());
314 void WebViewGuest::DidDropLink(const GURL& url) {
315 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
316 args->SetString(guest_view::kUrl, url.spec());
317 DispatchEventToView(
318 new GuestViewEvent(webview::kEventDropLink, args.Pass()));
321 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
322 script_executor_.reset(
323 new ScriptExecutor(web_contents(), &script_observers_));
325 notification_registrar_.Add(this,
326 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
327 content::Source<WebContents>(web_contents()));
329 notification_registrar_.Add(this,
330 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
331 content::Source<WebContents>(web_contents()));
333 AttachWebViewHelpers(web_contents());
335 rules_registry_id_ = GetOrGenerateRulesRegistryID(
336 owner_web_contents()->GetRenderProcessHost()->GetID(),
337 view_instance_id());
339 // We must install the mapping from guests to WebViews prior to resuming
340 // suspended resource loads so that the WebRequest API will catch resource
341 // requests.
342 PushWebViewStateToIOThread();
344 ApplyAttributes(create_params);
347 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
348 if (web_view_guest_delegate_)
349 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
350 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
353 void WebViewGuest::ClearDataInternal(base::Time remove_since,
354 uint32 removal_mask,
355 const base::Closure& callback) {
356 uint32 storage_partition_removal_mask =
357 GetStoragePartitionRemovalMask(removal_mask);
358 if (!storage_partition_removal_mask) {
359 callback.Run();
360 return;
362 content::StoragePartition* partition =
363 content::BrowserContext::GetStoragePartition(
364 web_contents()->GetBrowserContext(),
365 web_contents()->GetSiteInstance());
366 partition->ClearData(
367 storage_partition_removal_mask,
368 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, GURL(),
369 content::StoragePartition::OriginMatcherFunction(), remove_since,
370 base::Time::Now(), callback);
373 void WebViewGuest::GuestViewDidStopLoading() {
374 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
375 DispatchEventToView(
376 new GuestViewEvent(webview::kEventLoadStop, args.Pass()));
379 void WebViewGuest::EmbedderFullscreenToggled(bool entered_fullscreen) {
380 is_embedder_fullscreen_ = entered_fullscreen;
381 // If the embedder has got out of fullscreen, we get out of fullscreen
382 // mode as well.
383 if (!entered_fullscreen)
384 SetFullscreenState(false);
387 void WebViewGuest::EmbedderWillBeDestroyed() {
388 // Clean up rules registries for the webview.
389 RulesRegistryService::Get(browser_context())
390 ->RemoveRulesRegistriesByID(rules_registry_id_);
391 WebViewKey key(owner_web_contents()->GetRenderProcessHost()->GetID(),
392 view_instance_id());
393 web_view_key_to_id_map.Get().erase(key);
395 content::BrowserThread::PostTask(
396 content::BrowserThread::IO,
397 FROM_HERE,
398 base::Bind(
399 &RemoveWebViewEventListenersOnIOThread,
400 browser_context(),
401 owner_host(),
402 owner_web_contents()->GetRenderProcessHost()->GetID(),
403 view_instance_id()));
406 const char* WebViewGuest::GetAPINamespace() const {
407 return webview::kAPINamespace;
410 int WebViewGuest::GetTaskPrefix() const {
411 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
414 void WebViewGuest::GuestDestroyed() {
415 // Clean up custom context menu items for this guest.
416 if (web_view_guest_delegate_)
417 web_view_guest_delegate_->OnGuestDestroyed();
418 RemoveWebViewStateFromIOThread(web_contents());
421 void WebViewGuest::GuestReady() {
422 // The guest RenderView should always live in an isolated guest process.
423 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
424 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
426 // We don't want to accidentally set the opacity of an interstitial page.
427 // WebContents::GetRenderWidgetHostView will return the RWHV of an
428 // interstitial page if one is showing at this time. We only want opacity
429 // to apply to web pages.
430 if (allow_transparency_) {
431 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
432 SK_ColorTRANSPARENT);
433 } else {
434 web_contents()
435 ->GetRenderViewHost()
436 ->GetView()
437 ->SetBackgroundColorToDefault();
441 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
442 const gfx::Size& new_size) {
443 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
444 args->SetInteger(webview::kOldHeight, old_size.height());
445 args->SetInteger(webview::kOldWidth, old_size.width());
446 args->SetInteger(webview::kNewHeight, new_size.height());
447 args->SetInteger(webview::kNewWidth, new_size.width());
448 DispatchEventToView(
449 new GuestViewEvent(webview::kEventSizeChanged, args.Pass()));
452 bool WebViewGuest::IsAutoSizeSupported() const {
453 return true;
456 bool WebViewGuest::IsDragAndDropEnabled() const {
457 return true;
460 void WebViewGuest::GuestZoomChanged(double old_zoom_level,
461 double new_zoom_level) {
462 // Dispatch the zoomchange event.
463 double old_zoom_factor = ConvertZoomLevelToZoomFactor(old_zoom_level);
464 double new_zoom_factor = ConvertZoomLevelToZoomFactor(new_zoom_level);
465 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
466 args->SetDouble(webview::kOldZoomFactor, old_zoom_factor);
467 args->SetDouble(webview::kNewZoomFactor, new_zoom_factor);
468 DispatchEventToView(
469 new GuestViewEvent(webview::kEventZoomChange, args.Pass()));
472 void WebViewGuest::WillDestroy() {
473 if (!attached() && GetOpener())
474 GetOpener()->pending_new_windows_.erase(this);
477 bool WebViewGuest::AddMessageToConsole(WebContents* source,
478 int32 level,
479 const base::string16& message,
480 int32 line_no,
481 const base::string16& source_id) {
482 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
483 // Log levels are from base/logging.h: LogSeverity.
484 args->SetInteger(webview::kLevel, level);
485 args->SetString(webview::kMessage, message);
486 args->SetInteger(webview::kLine, line_no);
487 args->SetString(webview::kSourceId, source_id);
488 DispatchEventToView(
489 new GuestViewEvent(webview::kEventConsoleMessage, args.Pass()));
490 return true;
493 void WebViewGuest::CloseContents(WebContents* source) {
494 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
495 DispatchEventToView(
496 new GuestViewEvent(webview::kEventClose, args.Pass()));
499 void WebViewGuest::FindReply(WebContents* source,
500 int request_id,
501 int number_of_matches,
502 const gfx::Rect& selection_rect,
503 int active_match_ordinal,
504 bool final_update) {
505 find_helper_.FindReply(request_id,
506 number_of_matches,
507 selection_rect,
508 active_match_ordinal,
509 final_update);
512 double WebViewGuest::GetZoom() const {
513 double zoom_level =
514 ZoomController::FromWebContents(web_contents())->GetZoomLevel();
515 return ConvertZoomLevelToZoomFactor(zoom_level);
518 ZoomController::ZoomMode WebViewGuest::GetZoomMode() {
519 return ZoomController::FromWebContents(web_contents())->zoom_mode();
522 bool WebViewGuest::HandleContextMenu(
523 const content::ContextMenuParams& params) {
524 if (!web_view_guest_delegate_)
525 return false;
526 return web_view_guest_delegate_->HandleContextMenu(params);
529 void WebViewGuest::HandleKeyboardEvent(
530 WebContents* source,
531 const content::NativeWebKeyboardEvent& event) {
532 if (HandleKeyboardShortcuts(event))
533 return;
535 GuestViewBase::HandleKeyboardEvent(source, event);
538 bool WebViewGuest::PreHandleGestureEvent(content::WebContents* source,
539 const blink::WebGestureEvent& event) {
540 return !allow_scaling_ && GuestViewBase::PreHandleGestureEvent(source, event);
543 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
544 double progress) {
545 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
546 args->SetString(guest_view::kUrl, web_contents()->GetURL().spec());
547 args->SetDouble(webview::kProgress, progress);
548 DispatchEventToView(
549 new GuestViewEvent(webview::kEventLoadProgress, args.Pass()));
552 void WebViewGuest::LoadAbort(bool is_top_level,
553 const GURL& url,
554 int error_code,
555 const std::string& error_type) {
556 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
557 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
558 args->SetString(guest_view::kUrl, url.possibly_invalid_spec());
559 args->SetInteger(guest_view::kCode, error_code);
560 args->SetString(guest_view::kReason, error_type);
561 DispatchEventToView(
562 new GuestViewEvent(webview::kEventLoadAbort, args.Pass()));
565 void WebViewGuest::CreateNewGuestWebViewWindow(
566 const content::OpenURLParams& params) {
567 GuestViewManager* guest_manager =
568 GuestViewManager::FromBrowserContext(browser_context());
569 // Set the attach params to use the same partition as the opener.
570 // We pull the partition information from the site's URL, which is of the
571 // form guest://site/{persist}?{partition_name}.
572 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
573 const std::string storage_partition_id =
574 GetStoragePartitionIdFromSiteURL(site_url);
575 base::DictionaryValue create_params;
576 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
578 guest_manager->CreateGuest(WebViewGuest::Type,
579 embedder_web_contents(),
580 create_params,
581 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
582 weak_ptr_factory_.GetWeakPtr(),
583 params));
586 void WebViewGuest::NewGuestWebViewCallback(
587 const content::OpenURLParams& params,
588 content::WebContents* guest_web_contents) {
589 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
590 new_guest->SetOpener(this);
592 // Take ownership of |new_guest|.
593 pending_new_windows_.insert(
594 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
596 // Request permission to show the new window.
597 RequestNewWindowPermission(params.disposition,
598 gfx::Rect(),
599 params.user_gesture,
600 new_guest->web_contents());
603 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
604 // 'unresponsive' events.
605 void WebViewGuest::RendererResponsive(content::WebContents* source) {
606 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
607 args->SetInteger(webview::kProcessId,
608 web_contents()->GetRenderProcessHost()->GetID());
609 DispatchEventToView(
610 new GuestViewEvent(webview::kEventResponsive, args.Pass()));
613 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
614 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
615 args->SetInteger(webview::kProcessId,
616 web_contents()->GetRenderProcessHost()->GetID());
617 DispatchEventToView(
618 new GuestViewEvent(webview::kEventUnresponsive, args.Pass()));
621 void WebViewGuest::Observe(int type,
622 const content::NotificationSource& source,
623 const content::NotificationDetails& details) {
624 switch (type) {
625 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
626 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
627 if (content::Source<WebContents>(source).ptr() == web_contents())
628 LoadHandlerCalled();
629 break;
631 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
632 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
633 content::ResourceRedirectDetails* resource_redirect_details =
634 content::Details<content::ResourceRedirectDetails>(details).ptr();
635 bool is_top_level = resource_redirect_details->resource_type ==
636 content::RESOURCE_TYPE_MAIN_FRAME;
637 LoadRedirect(resource_redirect_details->url,
638 resource_redirect_details->new_url,
639 is_top_level);
640 break;
642 default:
643 NOTREACHED() << "Unexpected notification sent.";
644 break;
648 void WebViewGuest::StartFindInternal(
649 const base::string16& search_text,
650 const blink::WebFindOptions& options,
651 scoped_refptr<WebViewInternalFindFunction> find_function) {
652 find_helper_.Find(web_contents(), search_text, options, find_function);
655 void WebViewGuest::StopFindingInternal(content::StopFindAction action) {
656 find_helper_.CancelAllFindSessions();
657 web_contents()->StopFinding(action);
660 bool WebViewGuest::Go(int relative_index) {
661 content::NavigationController& controller = web_contents()->GetController();
662 if (!controller.CanGoToOffset(relative_index))
663 return false;
665 controller.GoToOffset(relative_index);
666 return true;
669 void WebViewGuest::Reload() {
670 // TODO(fsamuel): Don't check for repost because we don't want to show
671 // Chromium's repost warning. We might want to implement a separate API
672 // for registering a callback if a repost is about to happen.
673 web_contents()->GetController().Reload(false);
676 void WebViewGuest::SetUserAgentOverride(
677 const std::string& user_agent_override) {
678 is_overriding_user_agent_ = !user_agent_override.empty();
679 if (is_overriding_user_agent_) {
680 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
682 web_contents()->SetUserAgentOverride(user_agent_override);
685 void WebViewGuest::Stop() {
686 web_contents()->Stop();
689 void WebViewGuest::Terminate() {
690 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
691 base::ProcessHandle process_handle =
692 web_contents()->GetRenderProcessHost()->GetHandle();
693 if (process_handle)
694 web_contents()->GetRenderProcessHost()->Shutdown(
695 content::RESULT_CODE_KILLED, false);
698 bool WebViewGuest::ClearData(base::Time remove_since,
699 uint32 removal_mask,
700 const base::Closure& callback) {
701 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
702 content::StoragePartition* partition =
703 content::BrowserContext::GetStoragePartition(
704 web_contents()->GetBrowserContext(),
705 web_contents()->GetSiteInstance());
707 if (!partition)
708 return false;
710 if (removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_CACHE) {
711 // First clear http cache data and then clear the rest in
712 // |ClearDataInternal|.
713 int render_process_id = web_contents()->GetRenderProcessHost()->GetID();
714 // We need to clear renderer cache separately for our process because
715 // StoragePartitionHttpCacheDataRemover::ClearData() does not clear that.
716 web_cache::WebCacheManager::GetInstance()->Remove(render_process_id);
717 web_cache::WebCacheManager::GetInstance()->ClearCacheForProcess(
718 render_process_id);
720 base::Closure cache_removal_done_callback = base::Bind(
721 &WebViewGuest::ClearDataInternal, weak_ptr_factory_.GetWeakPtr(),
722 remove_since, removal_mask, callback);
723 // StoragePartitionHttpCacheDataRemover removes itself when it is done.
724 // components/, move |ClearCache| to WebViewGuest: http//crbug.com/471287.
725 browsing_data::StoragePartitionHttpCacheDataRemover::CreateForRange(
726 partition, remove_since, base::Time::Now())
727 ->Remove(cache_removal_done_callback);
729 return true;
732 ClearDataInternal(remove_since, removal_mask, callback);
733 return true;
736 WebViewGuest::WebViewGuest(content::WebContents* owner_web_contents)
737 : GuestView<WebViewGuest>(owner_web_contents),
738 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
739 find_helper_(this),
740 is_overriding_user_agent_(false),
741 allow_transparency_(false),
742 javascript_dialog_helper_(this),
743 allow_scaling_(false),
744 is_guest_fullscreen_(false),
745 is_embedder_fullscreen_(false),
746 last_fullscreen_permission_was_allowed_by_embedder_(false),
747 pending_zoom_factor_(0.0),
748 weak_ptr_factory_(this) {
749 web_view_guest_delegate_.reset(
750 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
753 WebViewGuest::~WebViewGuest() {
756 void WebViewGuest::DidCommitProvisionalLoadForFrame(
757 content::RenderFrameHost* render_frame_host,
758 const GURL& url,
759 ui::PageTransition transition_type) {
760 if (!render_frame_host->GetParent()) {
761 src_ = url;
762 // Handle a pending zoom if one exists.
763 if (pending_zoom_factor_) {
764 SetZoom(pending_zoom_factor_);
765 pending_zoom_factor_ = 0.0;
768 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
769 args->SetString(guest_view::kUrl, url.spec());
770 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
771 args->SetString(webview::kInternalBaseURLForDataURL,
772 web_contents()
773 ->GetController()
774 .GetLastCommittedEntry()
775 ->GetBaseURLForDataURL()
776 .spec());
777 args->SetInteger(webview::kInternalCurrentEntryIndex,
778 web_contents()->GetController().GetCurrentEntryIndex());
779 args->SetInteger(webview::kInternalEntryCount,
780 web_contents()->GetController().GetEntryCount());
781 args->SetInteger(webview::kInternalProcessId,
782 web_contents()->GetRenderProcessHost()->GetID());
783 DispatchEventToView(
784 new GuestViewEvent(webview::kEventLoadCommit, args.Pass()));
786 find_helper_.CancelAllFindSessions();
789 void WebViewGuest::DidFailProvisionalLoad(
790 content::RenderFrameHost* render_frame_host,
791 const GURL& validated_url,
792 int error_code,
793 const base::string16& error_description) {
794 LoadAbort(!render_frame_host->GetParent(), validated_url, error_code,
795 net::ErrorToShortString(error_code));
798 void WebViewGuest::DidStartProvisionalLoadForFrame(
799 content::RenderFrameHost* render_frame_host,
800 const GURL& validated_url,
801 bool is_error_page,
802 bool is_iframe_srcdoc) {
803 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
804 args->SetString(guest_view::kUrl, validated_url.spec());
805 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
806 DispatchEventToView(
807 new GuestViewEvent(webview::kEventLoadStart, args.Pass()));
810 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
811 // Cancel all find sessions in progress.
812 find_helper_.CancelAllFindSessions();
814 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
815 args->SetInteger(webview::kProcessId,
816 web_contents()->GetRenderProcessHost()->GetID());
817 args->SetString(webview::kReason, TerminationStatusToString(status));
818 DispatchEventToView(
819 new GuestViewEvent(webview::kEventExit, args.Pass()));
822 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
823 content::NavigationController& controller = web_contents()->GetController();
824 content::NavigationEntry* entry = controller.GetVisibleEntry();
825 if (!entry)
826 return;
827 entry->SetIsOverridingUserAgent(!user_agent.empty());
828 web_contents()->GetController().Reload(false);
831 void WebViewGuest::FrameNameChanged(RenderFrameHost* render_frame_host,
832 const std::string& name) {
833 if (render_frame_host->GetParent())
834 return;
836 if (name_ == name)
837 return;
839 ReportFrameNameChange(name);
842 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
843 name_ = name;
844 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
845 args->SetString(webview::kName, name);
846 DispatchEventToView(
847 new GuestViewEvent(webview::kEventFrameNameChanged, args.Pass()));
850 void WebViewGuest::LoadHandlerCalled() {
851 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
852 DispatchEventToView(
853 new GuestViewEvent(webview::kEventContentLoad, args.Pass()));
856 void WebViewGuest::LoadRedirect(const GURL& old_url,
857 const GURL& new_url,
858 bool is_top_level) {
859 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
860 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
861 args->SetString(webview::kNewURL, new_url.spec());
862 args->SetString(webview::kOldURL, old_url.spec());
863 DispatchEventToView(
864 new GuestViewEvent(webview::kEventLoadRedirect, args.Pass()));
867 void WebViewGuest::PushWebViewStateToIOThread() {
868 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
869 std::string partition_domain;
870 std::string partition_id;
871 bool in_memory;
872 if (!GetGuestPartitionConfigForSite(
873 site_url, &partition_domain, &partition_id, &in_memory)) {
874 NOTREACHED();
875 return;
878 WebViewRendererState::WebViewInfo web_view_info;
879 web_view_info.embedder_process_id =
880 owner_web_contents()->GetRenderProcessHost()->GetID();
881 web_view_info.instance_id = view_instance_id();
882 web_view_info.partition_id = partition_id;
883 web_view_info.owner_host = owner_host();
884 web_view_info.rules_registry_id = rules_registry_id_;
886 // Get content scripts IDs added by the guest.
887 WebViewContentScriptManager* manager =
888 WebViewContentScriptManager::Get(browser_context());
889 DCHECK(manager);
890 web_view_info.content_script_ids = manager->GetContentScriptIDSet(
891 web_view_info.embedder_process_id, web_view_info.instance_id);
893 content::BrowserThread::PostTask(
894 content::BrowserThread::IO,
895 FROM_HERE,
896 base::Bind(&WebViewRendererState::AddGuest,
897 base::Unretained(WebViewRendererState::GetInstance()),
898 web_contents()->GetRenderProcessHost()->GetID(),
899 web_contents()->GetRoutingID(),
900 web_view_info));
903 // static
904 void WebViewGuest::RemoveWebViewStateFromIOThread(
905 WebContents* web_contents) {
906 content::BrowserThread::PostTask(
907 content::BrowserThread::IO, FROM_HERE,
908 base::Bind(
909 &WebViewRendererState::RemoveGuest,
910 base::Unretained(WebViewRendererState::GetInstance()),
911 web_contents->GetRenderProcessHost()->GetID(),
912 web_contents->GetRoutingID()));
915 void WebViewGuest::RequestMediaAccessPermission(
916 content::WebContents* source,
917 const content::MediaStreamRequest& request,
918 const content::MediaResponseCallback& callback) {
919 web_view_permission_helper_->RequestMediaAccessPermission(source,
920 request,
921 callback);
924 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
925 const GURL& security_origin,
926 content::MediaStreamType type) {
927 return web_view_permission_helper_->CheckMediaAccessPermission(
928 source, security_origin, type);
931 void WebViewGuest::CanDownload(
932 const GURL& url,
933 const std::string& request_method,
934 const base::Callback<void(bool)>& callback) {
935 web_view_permission_helper_->CanDownload(url, request_method, callback);
938 void WebViewGuest::RequestPointerLockPermission(
939 bool user_gesture,
940 bool last_unlocked_by_target,
941 const base::Callback<void(bool)>& callback) {
942 web_view_permission_helper_->RequestPointerLockPermission(
943 user_gesture,
944 last_unlocked_by_target,
945 callback);
948 void WebViewGuest::SignalWhenReady(const base::Closure& callback) {
949 auto manager = WebViewContentScriptManager::Get(browser_context());
950 manager->SignalOnScriptsLoaded(callback);
953 void WebViewGuest::WillAttachToEmbedder() {
954 rules_registry_id_ = GetOrGenerateRulesRegistryID(
955 owner_web_contents()->GetRenderProcessHost()->GetID(),
956 view_instance_id());
958 // We must install the mapping from guests to WebViews prior to resuming
959 // suspended resource loads so that the WebRequest API will catch resource
960 // requests.
961 PushWebViewStateToIOThread();
964 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
965 WebContents* source) {
966 return &javascript_dialog_helper_;
969 void WebViewGuest::NavigateGuest(const std::string& src,
970 bool force_navigation) {
971 if (src.empty())
972 return;
974 GURL url = ResolveURL(src);
976 // We wait for all the content scripts to load and then navigate the guest
977 // if the navigation is embedder-initiated. For browser-initiated navigations,
978 // content scripts will be ready.
979 if (force_navigation) {
980 SignalWhenReady(
981 base::Bind(&WebViewGuest::LoadURLWithParams,
982 weak_ptr_factory_.GetWeakPtr(), url, content::Referrer(),
983 ui::PAGE_TRANSITION_AUTO_TOPLEVEL, force_navigation));
984 return;
986 LoadURLWithParams(url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
987 force_navigation);
990 bool WebViewGuest::HandleKeyboardShortcuts(
991 const content::NativeWebKeyboardEvent& event) {
992 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
993 if (!GuestViewManager::FromBrowserContext(browser_context())->
994 IsOwnedByExtension(this)) {
995 return false;
998 if (event.type != blink::WebInputEvent::RawKeyDown)
999 return false;
1001 // If the user hits the escape key without any modifiers then unlock the
1002 // mouse if necessary.
1003 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
1004 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
1005 return web_contents()->GotResponseToLockMouseRequest(false);
1008 #if defined(OS_MACOSX)
1009 if (event.modifiers != blink::WebInputEvent::MetaKey)
1010 return false;
1012 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
1013 Go(-1);
1014 return true;
1017 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
1018 Go(1);
1019 return true;
1021 #else
1022 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
1023 Go(-1);
1024 return true;
1027 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
1028 Go(1);
1029 return true;
1031 #endif
1033 return false;
1036 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
1037 std::string name;
1038 if (params.GetString(webview::kAttributeName, &name)) {
1039 // If the guest window's name is empty, then the WebView tag's name is
1040 // assigned. Otherwise, the guest window's name takes precedence over the
1041 // WebView tag's name.
1042 if (name_.empty())
1043 SetName(name);
1045 if (attached())
1046 ReportFrameNameChange(name_);
1048 std::string user_agent_override;
1049 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
1050 SetUserAgentOverride(user_agent_override);
1052 bool allow_transparency = false;
1053 if (params.GetBoolean(webview::kAttributeAllowTransparency,
1054 &allow_transparency)) {
1055 // We need to set the background opaque flag after navigation to ensure that
1056 // there is a RenderWidgetHostView available.
1057 SetAllowTransparency(allow_transparency);
1060 bool allow_scaling = false;
1061 if (params.GetBoolean(webview::kAttributeAllowScaling, &allow_scaling))
1062 SetAllowScaling(allow_scaling);
1064 // Check for a pending zoom from before the first navigation.
1065 params.GetDouble(webview::kInitialZoomFactor, &pending_zoom_factor_);
1067 bool is_pending_new_window = false;
1068 if (GetOpener()) {
1069 // We need to do a navigation here if the target URL has changed between
1070 // the time the WebContents was created and the time it was attached.
1071 // We also need to do an initial navigation if a RenderView was never
1072 // created for the new window in cases where there is no referrer.
1073 auto it = GetOpener()->pending_new_windows_.find(this);
1074 if (it != GetOpener()->pending_new_windows_.end()) {
1075 const NewWindowInfo& new_window_info = it->second;
1076 if (new_window_info.changed || !web_contents()->HasOpener())
1077 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
1079 // Once a new guest is attached to the DOM of the embedder page, then the
1080 // lifetime of the new guest is no longer managed by the opener guest.
1081 GetOpener()->pending_new_windows_.erase(this);
1083 is_pending_new_window = true;
1087 // Only read the src attribute if this is not a New Window API flow.
1088 if (!is_pending_new_window) {
1089 std::string src;
1090 if (params.GetString(webview::kAttributeSrc, &src))
1091 NavigateGuest(src, true /* force_navigation */);
1095 void WebViewGuest::ShowContextMenu(
1096 int request_id,
1097 const WebViewGuestDelegate::MenuItemVector* items) {
1098 if (web_view_guest_delegate_)
1099 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1102 void WebViewGuest::SetName(const std::string& name) {
1103 if (name_ == name)
1104 return;
1105 name_ = name;
1107 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1110 void WebViewGuest::SetZoom(double zoom_factor) {
1111 auto zoom_controller = ZoomController::FromWebContents(web_contents());
1112 DCHECK(zoom_controller);
1113 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1114 zoom_controller->SetZoomLevel(zoom_level);
1117 void WebViewGuest::SetZoomMode(ZoomController::ZoomMode zoom_mode) {
1118 ZoomController::FromWebContents(web_contents())->SetZoomMode(zoom_mode);
1121 void WebViewGuest::SetAllowTransparency(bool allow) {
1122 if (allow_transparency_ == allow)
1123 return;
1125 allow_transparency_ = allow;
1126 if (!web_contents()->GetRenderViewHost()->GetView())
1127 return;
1129 if (allow_transparency_) {
1130 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1131 SK_ColorTRANSPARENT);
1132 } else {
1133 web_contents()
1134 ->GetRenderViewHost()
1135 ->GetView()
1136 ->SetBackgroundColorToDefault();
1140 void WebViewGuest::SetAllowScaling(bool allow) {
1141 allow_scaling_ = allow;
1144 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1145 const std::string& base_url,
1146 const std::string& virtual_url,
1147 std::string* error) {
1148 // Make GURLs from URLs.
1149 const GURL data_gurl = GURL(data_url);
1150 const GURL base_gurl = GURL(base_url);
1151 const GURL virtual_gurl = GURL(virtual_url);
1153 // Check that the provided URLs are valid.
1154 // |data_url| must be a valid data URL.
1155 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1156 base::SStringPrintf(
1157 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1158 return false;
1160 // |base_url| must be a valid URL.
1161 if (!base_gurl.is_valid()) {
1162 base::SStringPrintf(
1163 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1164 return false;
1166 // |virtual_url| must be a valid URL.
1167 if (!virtual_gurl.is_valid()) {
1168 base::SStringPrintf(
1169 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1170 return false;
1173 // Set up the parameters to load |data_url| with the specified |base_url|.
1174 content::NavigationController::LoadURLParams load_params(data_gurl);
1175 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1176 load_params.base_url_for_data_url = base_gurl;
1177 load_params.virtual_url_for_data_url = virtual_gurl;
1178 load_params.override_user_agent =
1179 content::NavigationController::UA_OVERRIDE_INHERIT;
1181 // Navigate to the data URL.
1182 GuestViewBase::LoadURLWithParams(load_params);
1184 return true;
1187 void WebViewGuest::AddNewContents(content::WebContents* source,
1188 content::WebContents* new_contents,
1189 WindowOpenDisposition disposition,
1190 const gfx::Rect& initial_rect,
1191 bool user_gesture,
1192 bool* was_blocked) {
1193 if (was_blocked)
1194 *was_blocked = false;
1195 RequestNewWindowPermission(disposition,
1196 initial_rect,
1197 user_gesture,
1198 new_contents);
1201 content::WebContents* WebViewGuest::OpenURLFromTab(
1202 content::WebContents* source,
1203 const content::OpenURLParams& params) {
1204 // There are two use cases to consider from a security perspective:
1205 // 1.) Renderer-initiated navigation to chrome:// must always be blocked even
1206 // if the <webview> is in WebUI. This is handled by
1207 // WebViewGuest::LoadURLWithParams. WebViewGuest::NavigateGuest will also
1208 // call LoadURLWithParams. CreateNewGuestWebViewWindow creates a new
1209 // WebViewGuest which will call NavigateGuest in DidInitialize.
1210 // 2.) The Language Settings context menu item should always work, both in
1211 // Chrome Apps and WebUI. This is a browser initiated request and so
1212 // we pass it along to the embedder's WebContentsDelegate to get the
1213 // browser to perform the action for the <webview>.
1214 if (!params.is_renderer_initiated) {
1215 if (!owner_web_contents()->GetDelegate())
1216 return nullptr;
1217 return owner_web_contents()->GetDelegate()->OpenURLFromTab(
1218 owner_web_contents(), params);
1221 // If the guest wishes to navigate away prior to attachment then we save the
1222 // navigation to perform upon attachment. Navigation initializes a lot of
1223 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1224 // Navigation also resumes resource loading which we don't want to allow
1225 // until attachment.
1226 if (!attached()) {
1227 WebViewGuest* opener = GetOpener();
1228 auto it = opener->pending_new_windows_.find(this);
1229 if (it == opener->pending_new_windows_.end())
1230 return nullptr;
1231 const NewWindowInfo& info = it->second;
1232 NewWindowInfo new_window_info(params.url, info.name);
1233 new_window_info.changed = new_window_info.url != info.url;
1234 it->second = new_window_info;
1235 return nullptr;
1238 // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
1239 // decides that a fork should happen. At the time of writing this comment,
1240 // the only way a well behaving guest could hit this code path is if it
1241 // navigates to a URL that's associated with the default search engine.
1242 // This list of URLs is generated by chrome::GetSearchURLs. Validity checks
1243 // are performed inside LoadURLWithParams such that if the guest attempts
1244 // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
1245 // event will fire in the embedder, and the guest will be navigated to
1246 // about:blank.
1247 if (params.disposition == CURRENT_TAB) {
1248 LoadURLWithParams(params.url, params.referrer, params.transition,
1249 true /* force_navigation */);
1250 return web_contents();
1253 // This code path is taken if Ctrl+Click, middle click or any of the
1254 // keyboard/mouse combinations are used to open a link in a new tab/window.
1255 // This code path is also taken on client-side redirects from about:blank.
1256 CreateNewGuestWebViewWindow(params);
1257 return nullptr;
1260 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1261 int opener_render_frame_id,
1262 const base::string16& frame_name,
1263 const GURL& target_url,
1264 content::WebContents* new_contents) {
1265 auto guest = WebViewGuest::FromWebContents(new_contents);
1266 CHECK(guest);
1267 guest->SetOpener(this);
1268 std::string guest_name = base::UTF16ToUTF8(frame_name);
1269 guest->name_ = guest_name;
1270 pending_new_windows_.insert(
1271 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1274 void WebViewGuest::EnterFullscreenModeForTab(content::WebContents* web_contents,
1275 const GURL& origin) {
1276 // Ask the embedder for permission.
1277 base::DictionaryValue request_info;
1278 request_info.SetString(webview::kOrigin, origin.spec());
1279 web_view_permission_helper_->RequestPermission(
1280 WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, request_info,
1281 base::Bind(&WebViewGuest::OnFullscreenPermissionDecided,
1282 weak_ptr_factory_.GetWeakPtr()),
1283 false /* allowed_by_default */);
1285 // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
1286 // bounds. If the embedder denies the permission then we will see a flicker.
1287 // Once we have the ability to "cancel" a renderer/ fullscreen request:
1288 // http://crbug.com/466854 this won't be necessary and we should be
1289 // Calling SetFullscreenState(true) once the embedder allowed the request.
1290 // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
1291 SetFullscreenState(true);
1294 void WebViewGuest::ExitFullscreenModeForTab(
1295 content::WebContents* web_contents) {
1296 SetFullscreenState(false);
1299 bool WebViewGuest::IsFullscreenForTabOrPending(
1300 const content::WebContents* web_contents) const {
1301 return is_guest_fullscreen_;
1304 void WebViewGuest::LoadURLWithParams(const GURL& url,
1305 const content::Referrer& referrer,
1306 ui::PageTransition transition_type,
1307 bool force_navigation) {
1308 // Do not allow navigating a guest to schemes other than known safe schemes.
1309 // This will block the embedder trying to load unwanted schemes, e.g.
1310 // chrome://.
1311 bool scheme_is_blocked =
1312 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1313 url.scheme()) &&
1314 !url.SchemeIs(url::kAboutScheme)) ||
1315 url.SchemeIs(url::kJavaScriptScheme);
1316 if (scheme_is_blocked || !url.is_valid()) {
1317 LoadAbort(true /* is_top_level */, url, net::ERR_ABORTED,
1318 net::ErrorToShortString(net::ERR_ABORTED));
1319 NavigateGuest(url::kAboutBlankURL, false /* force_navigation */);
1320 return;
1323 if (!force_navigation && (src_ == url))
1324 return;
1326 GURL validated_url(url);
1327 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
1328 // As guests do not swap processes on navigation, only navigations to
1329 // normal web URLs are supported. No protocol handlers are installed for
1330 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
1331 // can be granted to the guest process.
1332 content::NavigationController::LoadURLParams load_url_params(validated_url);
1333 load_url_params.referrer = referrer;
1334 load_url_params.transition_type = transition_type;
1335 load_url_params.extra_headers = std::string();
1336 if (is_overriding_user_agent_) {
1337 load_url_params.override_user_agent =
1338 content::NavigationController::UA_OVERRIDE_TRUE;
1340 GuestViewBase::LoadURLWithParams(load_url_params);
1342 src_ = validated_url;
1345 void WebViewGuest::RequestNewWindowPermission(
1346 WindowOpenDisposition disposition,
1347 const gfx::Rect& initial_bounds,
1348 bool user_gesture,
1349 content::WebContents* new_contents) {
1350 auto guest = WebViewGuest::FromWebContents(new_contents);
1351 if (!guest)
1352 return;
1353 auto it = pending_new_windows_.find(guest);
1354 if (it == pending_new_windows_.end())
1355 return;
1356 const NewWindowInfo& new_window_info = it->second;
1358 // Retrieve the opener partition info if we have it.
1359 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1360 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1362 base::DictionaryValue request_info;
1363 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1364 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1365 request_info.Set(webview::kTargetURL,
1366 new base::StringValue(new_window_info.url.spec()));
1367 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1368 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1369 // We pass in partition info so that window-s created through newwindow
1370 // API can use it to set their partition attribute.
1371 request_info.Set(webview::kStoragePartitionId,
1372 new base::StringValue(storage_partition_id));
1373 request_info.Set(
1374 webview::kWindowOpenDisposition,
1375 new base::StringValue(WindowOpenDispositionToString(disposition)));
1377 web_view_permission_helper_->
1378 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1379 request_info,
1380 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1381 weak_ptr_factory_.GetWeakPtr(),
1382 guest->guest_instance_id()),
1383 false /* allowed_by_default */);
1386 GURL WebViewGuest::ResolveURL(const std::string& src) {
1387 if (!GuestViewManager::FromBrowserContext(browser_context())->
1388 IsOwnedByExtension(this)) {
1389 return GURL(src);
1392 GURL default_url(base::StringPrintf("%s://%s/",
1393 kExtensionScheme,
1394 owner_host().c_str()));
1395 return default_url.Resolve(src);
1398 void WebViewGuest::OnWebViewNewWindowResponse(
1399 int new_window_instance_id,
1400 bool allow,
1401 const std::string& user_input) {
1402 auto guest =
1403 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1404 new_window_instance_id);
1405 if (!guest)
1406 return;
1408 if (!allow)
1409 guest->Destroy();
1412 void WebViewGuest::OnFullscreenPermissionDecided(
1413 bool allowed,
1414 const std::string& user_input) {
1415 last_fullscreen_permission_was_allowed_by_embedder_ = allowed;
1416 SetFullscreenState(allowed);
1419 bool WebViewGuest::GuestMadeEmbedderFullscreen() const {
1420 return last_fullscreen_permission_was_allowed_by_embedder_ &&
1421 is_embedder_fullscreen_;
1424 void WebViewGuest::SetFullscreenState(bool is_fullscreen) {
1425 if (is_fullscreen == is_guest_fullscreen_)
1426 return;
1428 bool was_fullscreen = is_guest_fullscreen_;
1429 is_guest_fullscreen_ = is_fullscreen;
1430 // If the embedder entered fullscreen because of us, it should exit fullscreen
1431 // when we exit fullscreen.
1432 if (was_fullscreen && GuestMadeEmbedderFullscreen()) {
1433 // Dispatch a message so we can call document.webkitCancelFullscreen()
1434 // on the embedder.
1435 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1436 DispatchEventToView(
1437 new GuestViewEvent(webview::kEventExitFullscreen, args.Pass()));
1439 // Since we changed fullscreen state, sending a Resize message ensures that
1440 // renderer/ sees the change.
1441 web_contents()->GetRenderViewHost()->WasResized();
1444 } // namespace extensions