Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blobda15b9fa92d4571d3a5dc28e216887b8c6b37452
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/browsing_data/storage_partition_http_cache_data_remover.h"
11 #include "components/guest_view/browser/guest_view_event.h"
12 #include "components/guest_view/browser/guest_view_manager.h"
13 #include "components/guest_view/common/guest_view_constants.h"
14 #include "components/web_cache/browser/web_cache_manager.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_security_policy.h"
18 #include "content/public/browser/native_web_keyboard_event.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/browser/resource_request_details.h"
28 #include "content/public/browser/site_instance.h"
29 #include "content/public/browser/storage_partition.h"
30 #include "content/public/browser/user_metrics.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/common/media_stream_request.h"
34 #include "content/public/common/page_zoom.h"
35 #include "content/public/common/result_codes.h"
36 #include "content/public/common/stop_find_action.h"
37 #include "content/public/common/url_constants.h"
38 #include "extensions/browser/api/declarative/rules_registry_service.h"
39 #include "extensions/browser/api/extensions_api_client.h"
40 #include "extensions/browser/api/guest_view/web_view/web_view_internal_api.h"
41 #include "extensions/browser/api/web_request/web_request_api.h"
42 #include "extensions/browser/extension_system.h"
43 #include "extensions/browser/extensions_browser_client.h"
44 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
45 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
46 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
47 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
48 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
49 #include "extensions/common/constants.h"
50 #include "extensions/common/extension_messages.h"
51 #include "extensions/strings/grit/extensions_strings.h"
52 #include "ipc/ipc_message_macros.h"
53 #include "net/base/escape.h"
54 #include "net/base/net_errors.h"
55 #include "ui/base/models/simple_menu_model.h"
56 #include "url/url_constants.h"
58 using base::UserMetricsAction;
59 using content::GlobalRequestID;
60 using content::RenderFrameHost;
61 using content::ResourceType;
62 using content::StoragePartition;
63 using content::WebContents;
64 using guest_view::GuestViewBase;
65 using guest_view::GuestViewEvent;
66 using guest_view::GuestViewManager;
67 using ui_zoom::ZoomController;
69 namespace extensions {
71 namespace {
73 // Returns storage partition removal mask from web_view clearData mask. Note
74 // that storage partition mask is a subset of webview's data removal mask.
75 uint32 GetStoragePartitionRemovalMask(uint32 web_view_removal_mask) {
76 uint32 mask = 0;
77 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_APPCACHE)
78 mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
79 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_COOKIES)
80 mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
81 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS)
82 mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
83 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB)
84 mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
85 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE)
86 mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
87 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL)
88 mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
90 return mask;
93 std::string WindowOpenDispositionToString(
94 WindowOpenDisposition window_open_disposition) {
95 switch (window_open_disposition) {
96 case IGNORE_ACTION:
97 return "ignore";
98 case SAVE_TO_DISK:
99 return "save_to_disk";
100 case CURRENT_TAB:
101 return "current_tab";
102 case NEW_BACKGROUND_TAB:
103 return "new_background_tab";
104 case NEW_FOREGROUND_TAB:
105 return "new_foreground_tab";
106 case NEW_WINDOW:
107 return "new_window";
108 case NEW_POPUP:
109 return "new_popup";
110 default:
111 NOTREACHED() << "Unknown Window Open Disposition";
112 return "ignore";
116 static std::string TerminationStatusToString(base::TerminationStatus status) {
117 switch (status) {
118 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
119 return "normal";
120 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
121 case base::TERMINATION_STATUS_STILL_RUNNING:
122 return "abnormal";
123 #if defined(OS_CHROMEOS)
124 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
125 return "oom killed";
126 #endif
127 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
128 return "killed";
129 case base::TERMINATION_STATUS_PROCESS_CRASHED:
130 return "crashed";
131 case base::TERMINATION_STATUS_MAX_ENUM:
132 break;
134 NOTREACHED() << "Unknown Termination Status.";
135 return "unknown";
138 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
139 const std::string& partition_id = site_url.query();
140 bool persist_storage = site_url.path().find("persist") != std::string::npos;
141 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
144 void ParsePartitionParam(const base::DictionaryValue& create_params,
145 std::string* storage_partition_id,
146 bool* persist_storage) {
147 std::string partition_str;
148 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
149 return;
152 // Since the "persist:" prefix is in ASCII, base::StartsWith will work fine on
153 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
154 // remove the prefix without splicing in the middle of a multi-byte codepoint.
155 // We can use the rest of the string as UTF-8 encoded one.
156 if (base::StartsWith(partition_str, "persist:",
157 base::CompareCase::SENSITIVE)) {
158 size_t index = partition_str.find(":");
159 CHECK(index != std::string::npos);
160 // It is safe to do index + 1, since we tested for the full prefix above.
161 *storage_partition_id = partition_str.substr(index + 1);
163 if (storage_partition_id->empty()) {
164 // TODO(lazyboy): Better way to deal with this error.
165 return;
167 *persist_storage = true;
168 } else {
169 *storage_partition_id = partition_str;
170 *persist_storage = false;
174 void RemoveWebViewEventListenersOnIOThread(
175 void* profile,
176 int embedder_process_id,
177 int view_instance_id) {
178 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
179 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
180 profile,
181 embedder_process_id,
182 view_instance_id);
185 double ConvertZoomLevelToZoomFactor(double zoom_level) {
186 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
187 // Because the conversion from zoom level to zoom factor isn't perfect, the
188 // resulting zoom factor is rounded to the nearest 6th decimal place.
189 zoom_factor = round(zoom_factor * 1000000) / 1000000;
190 return zoom_factor;
193 using WebViewKey = std::pair<int, int>;
194 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
195 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
196 LAZY_INSTANCE_INITIALIZER;
198 } // namespace
200 // static
201 void WebViewGuest::CleanUp(content::BrowserContext* browser_context,
202 int embedder_process_id,
203 int view_instance_id) {
204 GuestViewBase::CleanUp(browser_context, embedder_process_id,
205 view_instance_id);
207 // Clean up rules registries for the WebView.
208 WebViewKey key(embedder_process_id, view_instance_id);
209 auto it = web_view_key_to_id_map.Get().find(key);
210 if (it != web_view_key_to_id_map.Get().end()) {
211 auto rules_registry_id = it->second;
212 web_view_key_to_id_map.Get().erase(it);
213 RulesRegistryService* rrs =
214 RulesRegistryService::GetIfExists(browser_context);
215 if (rrs)
216 rrs->RemoveRulesRegistriesByID(rules_registry_id);
219 // Clean up web request event listeners for the WebView.
220 content::BrowserThread::PostTask(
221 content::BrowserThread::IO,
222 FROM_HERE,
223 base::Bind(
224 &RemoveWebViewEventListenersOnIOThread,
225 browser_context,
226 embedder_process_id,
227 view_instance_id));
229 // Clean up content scripts for the WebView.
230 auto csm = WebViewContentScriptManager::Get(browser_context);
231 csm->RemoveAllContentScriptsForWebView(embedder_process_id, view_instance_id);
233 // Allow an extensions browser client to potentially perform more cleanup.
234 ExtensionsBrowserClient::Get()->CleanUpWebView(
235 browser_context, embedder_process_id, view_instance_id);
238 // static
239 GuestViewBase* WebViewGuest::Create(WebContents* owner_web_contents) {
240 return new WebViewGuest(owner_web_contents);
243 // static
244 bool WebViewGuest::GetGuestPartitionConfigForSite(
245 const GURL& site,
246 std::string* partition_domain,
247 std::string* partition_name,
248 bool* in_memory) {
249 if (!site.SchemeIs(content::kGuestScheme))
250 return false;
252 // Since guest URLs are only used for packaged apps, there must be an app
253 // id in the URL.
254 CHECK(site.has_host());
255 *partition_domain = site.host();
256 // Since persistence is optional, the path must either be empty or the
257 // literal string.
258 *in_memory = (site.path() != "/persist");
259 // The partition name is user supplied value, which we have encoded when the
260 // URL was created, so it needs to be decoded.
261 *partition_name =
262 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
263 return true;
266 // static
267 const char WebViewGuest::Type[] = "webview";
269 // static
270 int WebViewGuest::GetOrGenerateRulesRegistryID(
271 int embedder_process_id,
272 int webview_instance_id) {
273 bool is_web_view = embedder_process_id && webview_instance_id;
274 if (!is_web_view)
275 return RulesRegistryService::kDefaultRulesRegistryID;
277 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
278 auto it = web_view_key_to_id_map.Get().find(key);
279 if (it != web_view_key_to_id_map.Get().end())
280 return it->second;
282 auto rph = content::RenderProcessHost::FromID(embedder_process_id);
283 int rules_registry_id =
284 RulesRegistryService::Get(rph->GetBrowserContext())->
285 GetNextRulesRegistryID();
286 web_view_key_to_id_map.Get()[key] = rules_registry_id;
287 return rules_registry_id;
290 bool WebViewGuest::CanRunInDetachedState() const {
291 return true;
294 void WebViewGuest::CreateWebContents(
295 const base::DictionaryValue& create_params,
296 const WebContentsCreatedCallback& callback) {
297 content::RenderProcessHost* owner_render_process_host =
298 owner_web_contents()->GetRenderProcessHost();
299 std::string storage_partition_id;
300 bool persist_storage = false;
301 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
302 // Validate that the partition id coming from the renderer is valid UTF-8,
303 // since we depend on this in other parts of the code, such as FilePath
304 // creation. If the validation fails, treat it as a bad message and kill the
305 // renderer process.
306 if (!base::IsStringUTF8(storage_partition_id)) {
307 content::RecordAction(
308 base::UserMetricsAction("BadMessageTerminate_BPGM"));
309 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
310 false);
311 callback.Run(nullptr);
312 return;
314 std::string url_encoded_partition = net::EscapeQueryParamValue(
315 storage_partition_id, false);
316 std::string partition_domain = GetOwnerSiteURL().host();
317 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
318 content::kGuestScheme,
319 partition_domain.c_str(),
320 persist_storage ? "persist" : "",
321 url_encoded_partition.c_str()));
323 // If we already have a webview tag in the same app using the same storage
324 // partition, we should use the same SiteInstance so the existing tag and
325 // the new tag can script each other.
326 auto guest_view_manager = GuestViewManager::FromBrowserContext(
327 owner_render_process_host->GetBrowserContext());
328 content::SiteInstance* guest_site_instance =
329 guest_view_manager->GetGuestSiteInstance(guest_site);
330 if (!guest_site_instance) {
331 // Create the SiteInstance in a new BrowsingInstance, which will ensure
332 // that webview tags are also not allowed to send messages across
333 // different partitions.
334 guest_site_instance = content::SiteInstance::CreateForURL(
335 owner_render_process_host->GetBrowserContext(), guest_site);
337 WebContents::CreateParams params(
338 owner_render_process_host->GetBrowserContext(),
339 guest_site_instance);
340 params.guest_delegate = this;
341 callback.Run(WebContents::Create(params));
344 void WebViewGuest::DidAttachToEmbedder() {
345 ApplyAttributes(*attach_params());
348 void WebViewGuest::DidDropLink(const GURL& url) {
349 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
350 args->SetString(guest_view::kUrl, url.spec());
351 DispatchEventToView(
352 new GuestViewEvent(webview::kEventDropLink, args.Pass()));
355 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
356 script_executor_.reset(
357 new ScriptExecutor(web_contents(), &script_observers_));
359 notification_registrar_.Add(this,
360 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
361 content::Source<WebContents>(web_contents()));
363 notification_registrar_.Add(this,
364 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
365 content::Source<WebContents>(web_contents()));
367 if (web_view_guest_delegate_)
368 web_view_guest_delegate_->OnDidInitialize();
369 ExtensionsAPIClient::Get()->AttachWebContentsHelpers(web_contents());
370 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
372 rules_registry_id_ = GetOrGenerateRulesRegistryID(
373 owner_web_contents()->GetRenderProcessHost()->GetID(),
374 view_instance_id());
376 // We must install the mapping from guests to WebViews prior to resuming
377 // suspended resource loads so that the WebRequest API will catch resource
378 // requests.
379 PushWebViewStateToIOThread();
381 ApplyAttributes(create_params);
384 void WebViewGuest::ClearDataInternal(base::Time remove_since,
385 uint32 removal_mask,
386 const base::Closure& callback) {
387 uint32 storage_partition_removal_mask =
388 GetStoragePartitionRemovalMask(removal_mask);
389 if (!storage_partition_removal_mask) {
390 callback.Run();
391 return;
393 content::StoragePartition* partition =
394 content::BrowserContext::GetStoragePartition(
395 web_contents()->GetBrowserContext(),
396 web_contents()->GetSiteInstance());
397 partition->ClearData(
398 storage_partition_removal_mask,
399 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, GURL(),
400 content::StoragePartition::OriginMatcherFunction(), remove_since,
401 base::Time::Now(), callback);
404 void WebViewGuest::GuestViewDidStopLoading() {
405 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
406 DispatchEventToView(
407 new GuestViewEvent(webview::kEventLoadStop, args.Pass()));
410 void WebViewGuest::EmbedderFullscreenToggled(bool entered_fullscreen) {
411 is_embedder_fullscreen_ = entered_fullscreen;
412 // If the embedder has got out of fullscreen, we get out of fullscreen
413 // mode as well.
414 if (!entered_fullscreen)
415 SetFullscreenState(false);
418 const char* WebViewGuest::GetAPINamespace() const {
419 return webview::kAPINamespace;
422 int WebViewGuest::GetTaskPrefix() const {
423 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
426 void WebViewGuest::GuestDestroyed() {
427 RemoveWebViewStateFromIOThread(web_contents());
430 void WebViewGuest::GuestReady() {
431 // The guest RenderView should always live in an isolated guest process.
432 CHECK(web_contents()->GetRenderProcessHost()->IsForGuestsOnly());
433 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
435 // We don't want to accidentally set the opacity of an interstitial page.
436 // WebContents::GetRenderWidgetHostView will return the RWHV of an
437 // interstitial page if one is showing at this time. We only want opacity
438 // to apply to web pages.
439 if (allow_transparency_) {
440 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
441 SK_ColorTRANSPARENT);
442 } else {
443 web_contents()
444 ->GetRenderViewHost()
445 ->GetView()
446 ->SetBackgroundColorToDefault();
450 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
451 const gfx::Size& new_size) {
452 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
453 args->SetInteger(webview::kOldHeight, old_size.height());
454 args->SetInteger(webview::kOldWidth, old_size.width());
455 args->SetInteger(webview::kNewHeight, new_size.height());
456 args->SetInteger(webview::kNewWidth, new_size.width());
457 DispatchEventToView(
458 new GuestViewEvent(webview::kEventSizeChanged, args.Pass()));
461 bool WebViewGuest::IsAutoSizeSupported() const {
462 return true;
465 void WebViewGuest::GuestZoomChanged(double old_zoom_level,
466 double new_zoom_level) {
467 // Dispatch the zoomchange event.
468 double old_zoom_factor = ConvertZoomLevelToZoomFactor(old_zoom_level);
469 double new_zoom_factor = ConvertZoomLevelToZoomFactor(new_zoom_level);
470 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
471 args->SetDouble(webview::kOldZoomFactor, old_zoom_factor);
472 args->SetDouble(webview::kNewZoomFactor, new_zoom_factor);
473 DispatchEventToView(
474 new GuestViewEvent(webview::kEventZoomChange, args.Pass()));
477 void WebViewGuest::WillDestroy() {
478 if (!attached() && GetOpener())
479 GetOpener()->pending_new_windows_.erase(this);
482 bool WebViewGuest::AddMessageToConsole(WebContents* source,
483 int32 level,
484 const base::string16& message,
485 int32 line_no,
486 const base::string16& source_id) {
487 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
488 // Log levels are from base/logging.h: LogSeverity.
489 args->SetInteger(webview::kLevel, level);
490 args->SetString(webview::kMessage, message);
491 args->SetInteger(webview::kLine, line_no);
492 args->SetString(webview::kSourceId, source_id);
493 DispatchEventToView(
494 new GuestViewEvent(webview::kEventConsoleMessage, args.Pass()));
495 return true;
498 void WebViewGuest::CloseContents(WebContents* source) {
499 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
500 DispatchEventToView(
501 new GuestViewEvent(webview::kEventClose, args.Pass()));
504 void WebViewGuest::FindReply(WebContents* source,
505 int request_id,
506 int number_of_matches,
507 const gfx::Rect& selection_rect,
508 int active_match_ordinal,
509 bool final_update) {
510 GuestViewBase::FindReply(source, request_id, number_of_matches,
511 selection_rect, active_match_ordinal, final_update);
512 find_helper_.FindReply(request_id, number_of_matches, selection_rect,
513 active_match_ordinal, final_update);
516 double WebViewGuest::GetZoom() const {
517 double zoom_level =
518 ZoomController::FromWebContents(web_contents())->GetZoomLevel();
519 return ConvertZoomLevelToZoomFactor(zoom_level);
522 ZoomController::ZoomMode WebViewGuest::GetZoomMode() {
523 return ZoomController::FromWebContents(web_contents())->zoom_mode();
526 bool WebViewGuest::HandleContextMenu(
527 const content::ContextMenuParams& params) {
528 if (!web_view_guest_delegate_)
529 return false;
530 return web_view_guest_delegate_->HandleContextMenu(params);
533 void WebViewGuest::HandleKeyboardEvent(
534 WebContents* source,
535 const content::NativeWebKeyboardEvent& event) {
536 if (HandleKeyboardShortcuts(event))
537 return;
539 GuestViewBase::HandleKeyboardEvent(source, event);
542 bool WebViewGuest::PreHandleGestureEvent(WebContents* source,
543 const blink::WebGestureEvent& event) {
544 return !allow_scaling_ && GuestViewBase::PreHandleGestureEvent(source, event);
547 void WebViewGuest::LoadProgressChanged(WebContents* source, double progress) {
548 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
549 args->SetString(guest_view::kUrl, web_contents()->GetURL().spec());
550 args->SetDouble(webview::kProgress, progress);
551 DispatchEventToView(
552 new GuestViewEvent(webview::kEventLoadProgress, args.Pass()));
555 void WebViewGuest::LoadAbort(bool is_top_level,
556 const GURL& url,
557 int error_code,
558 const std::string& error_type) {
559 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
560 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
561 args->SetString(guest_view::kUrl, url.possibly_invalid_spec());
562 args->SetInteger(guest_view::kCode, error_code);
563 args->SetString(guest_view::kReason, error_type);
564 DispatchEventToView(
565 new GuestViewEvent(webview::kEventLoadAbort, args.Pass()));
568 void WebViewGuest::CreateNewGuestWebViewWindow(
569 const content::OpenURLParams& params) {
570 GuestViewManager* guest_manager =
571 GuestViewManager::FromBrowserContext(browser_context());
572 // Set the attach params to use the same partition as the opener.
573 // We pull the partition information from the site's URL, which is of the
574 // form guest://site/{persist}?{partition_name}.
575 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
576 const std::string storage_partition_id =
577 GetStoragePartitionIdFromSiteURL(site_url);
578 base::DictionaryValue create_params;
579 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
581 guest_manager->CreateGuest(WebViewGuest::Type,
582 embedder_web_contents(),
583 create_params,
584 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
585 weak_ptr_factory_.GetWeakPtr(),
586 params));
589 void WebViewGuest::NewGuestWebViewCallback(const content::OpenURLParams& params,
590 WebContents* guest_web_contents) {
591 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
592 new_guest->SetOpener(this);
594 // Take ownership of |new_guest|.
595 pending_new_windows_.insert(
596 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
598 // Request permission to show the new window.
599 RequestNewWindowPermission(params.disposition,
600 gfx::Rect(),
601 params.user_gesture,
602 new_guest->web_contents());
605 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
606 // 'unresponsive' events.
607 void WebViewGuest::RendererResponsive(WebContents* source) {
608 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
609 args->SetInteger(webview::kProcessId,
610 web_contents()->GetRenderProcessHost()->GetID());
611 DispatchEventToView(
612 new GuestViewEvent(webview::kEventResponsive, args.Pass()));
615 void WebViewGuest::RendererUnresponsive(WebContents* source) {
616 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
617 args->SetInteger(webview::kProcessId,
618 web_contents()->GetRenderProcessHost()->GetID());
619 DispatchEventToView(
620 new GuestViewEvent(webview::kEventUnresponsive, args.Pass()));
623 void WebViewGuest::Observe(int type,
624 const content::NotificationSource& source,
625 const content::NotificationDetails& details) {
626 switch (type) {
627 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
628 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
629 if (content::Source<WebContents>(source).ptr() == web_contents())
630 LoadHandlerCalled();
631 break;
633 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
634 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
635 content::ResourceRedirectDetails* resource_redirect_details =
636 content::Details<content::ResourceRedirectDetails>(details).ptr();
637 bool is_top_level = resource_redirect_details->resource_type ==
638 content::RESOURCE_TYPE_MAIN_FRAME;
639 LoadRedirect(resource_redirect_details->url,
640 resource_redirect_details->new_url,
641 is_top_level);
642 break;
644 default:
645 NOTREACHED() << "Unexpected notification sent.";
646 break;
650 void WebViewGuest::StartFindInternal(
651 const base::string16& search_text,
652 const blink::WebFindOptions& options,
653 scoped_refptr<WebViewInternalFindFunction> find_function) {
654 find_helper_.Find(web_contents(), search_text, options, find_function);
657 void WebViewGuest::StopFindingInternal(content::StopFindAction action) {
658 find_helper_.CancelAllFindSessions();
659 web_contents()->StopFinding(action);
662 bool WebViewGuest::Go(int relative_index) {
663 content::NavigationController& controller = web_contents()->GetController();
664 if (!controller.CanGoToOffset(relative_index))
665 return false;
667 controller.GoToOffset(relative_index);
668 return true;
671 void WebViewGuest::Reload() {
672 // TODO(fsamuel): Don't check for repost because we don't want to show
673 // Chromium's repost warning. We might want to implement a separate API
674 // for registering a callback if a repost is about to happen.
675 web_contents()->GetController().Reload(false);
678 void WebViewGuest::SetUserAgentOverride(
679 const std::string& user_agent_override) {
680 is_overriding_user_agent_ = !user_agent_override.empty();
681 if (is_overriding_user_agent_) {
682 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
684 web_contents()->SetUserAgentOverride(user_agent_override);
687 void WebViewGuest::Stop() {
688 web_contents()->Stop();
691 void WebViewGuest::Terminate() {
692 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
693 base::ProcessHandle process_handle =
694 web_contents()->GetRenderProcessHost()->GetHandle();
695 if (process_handle)
696 web_contents()->GetRenderProcessHost()->Shutdown(
697 content::RESULT_CODE_KILLED, false);
700 bool WebViewGuest::ClearData(base::Time remove_since,
701 uint32 removal_mask,
702 const base::Closure& callback) {
703 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
704 content::StoragePartition* partition =
705 content::BrowserContext::GetStoragePartition(
706 web_contents()->GetBrowserContext(),
707 web_contents()->GetSiteInstance());
709 if (!partition)
710 return false;
712 if (removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_CACHE) {
713 // First clear http cache data and then clear the rest in
714 // |ClearDataInternal|.
715 int render_process_id = web_contents()->GetRenderProcessHost()->GetID();
716 // We need to clear renderer cache separately for our process because
717 // StoragePartitionHttpCacheDataRemover::ClearData() does not clear that.
718 web_cache::WebCacheManager::GetInstance()->Remove(render_process_id);
719 web_cache::WebCacheManager::GetInstance()->ClearCacheForProcess(
720 render_process_id);
722 base::Closure cache_removal_done_callback = base::Bind(
723 &WebViewGuest::ClearDataInternal, weak_ptr_factory_.GetWeakPtr(),
724 remove_since, removal_mask, callback);
725 // StoragePartitionHttpCacheDataRemover removes itself when it is done.
726 // components/, move |ClearCache| to WebViewGuest: http//crbug.com/471287.
727 browsing_data::StoragePartitionHttpCacheDataRemover::CreateForRange(
728 partition, remove_since, base::Time::Now())
729 ->Remove(cache_removal_done_callback);
731 return true;
734 ClearDataInternal(remove_since, removal_mask, callback);
735 return true;
738 WebViewGuest::WebViewGuest(WebContents* owner_web_contents)
739 : GuestView<WebViewGuest>(owner_web_contents),
740 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
741 find_helper_(this),
742 is_overriding_user_agent_(false),
743 allow_transparency_(false),
744 javascript_dialog_helper_(this),
745 allow_scaling_(false),
746 is_guest_fullscreen_(false),
747 is_embedder_fullscreen_(false),
748 last_fullscreen_permission_was_allowed_by_embedder_(false),
749 pending_zoom_factor_(0.0),
750 weak_ptr_factory_(this) {
751 web_view_guest_delegate_.reset(
752 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
755 WebViewGuest::~WebViewGuest() {
758 void WebViewGuest::DidCommitProvisionalLoadForFrame(
759 content::RenderFrameHost* render_frame_host,
760 const GURL& url,
761 ui::PageTransition transition_type) {
762 if (!render_frame_host->GetParent()) {
763 src_ = url;
764 // Handle a pending zoom if one exists.
765 if (pending_zoom_factor_) {
766 SetZoom(pending_zoom_factor_);
767 pending_zoom_factor_ = 0.0;
770 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
771 args->SetString(guest_view::kUrl, url.spec());
772 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
773 args->SetString(webview::kInternalBaseURLForDataURL,
774 web_contents()
775 ->GetController()
776 .GetLastCommittedEntry()
777 ->GetBaseURLForDataURL()
778 .spec());
779 args->SetInteger(webview::kInternalCurrentEntryIndex,
780 web_contents()->GetController().GetCurrentEntryIndex());
781 args->SetInteger(webview::kInternalEntryCount,
782 web_contents()->GetController().GetEntryCount());
783 args->SetInteger(webview::kInternalProcessId,
784 web_contents()->GetRenderProcessHost()->GetID());
785 DispatchEventToView(
786 new GuestViewEvent(webview::kEventLoadCommit, args.Pass()));
788 find_helper_.CancelAllFindSessions();
791 void WebViewGuest::DidFailProvisionalLoad(
792 content::RenderFrameHost* render_frame_host,
793 const GURL& validated_url,
794 int error_code,
795 const base::string16& error_description,
796 bool was_ignored_by_handler) {
797 // Suppress loadabort for "mailto" URLs.
798 if (validated_url.SchemeIs(url::kMailToScheme))
799 return;
801 LoadAbort(!render_frame_host->GetParent(), validated_url, error_code,
802 net::ErrorToShortString(error_code));
805 void WebViewGuest::DidStartProvisionalLoadForFrame(
806 content::RenderFrameHost* render_frame_host,
807 const GURL& validated_url,
808 bool is_error_page,
809 bool is_iframe_srcdoc) {
810 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
811 args->SetString(guest_view::kUrl, validated_url.spec());
812 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
813 DispatchEventToView(
814 new GuestViewEvent(webview::kEventLoadStart, args.Pass()));
817 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
818 // Cancel all find sessions in progress.
819 find_helper_.CancelAllFindSessions();
821 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
822 args->SetInteger(webview::kProcessId,
823 web_contents()->GetRenderProcessHost()->GetID());
824 args->SetString(webview::kReason, TerminationStatusToString(status));
825 DispatchEventToView(
826 new GuestViewEvent(webview::kEventExit, args.Pass()));
829 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
830 content::NavigationController& controller = web_contents()->GetController();
831 content::NavigationEntry* entry = controller.GetVisibleEntry();
832 if (!entry)
833 return;
834 entry->SetIsOverridingUserAgent(!user_agent.empty());
835 web_contents()->GetController().Reload(false);
838 void WebViewGuest::FrameNameChanged(RenderFrameHost* render_frame_host,
839 const std::string& name) {
840 if (render_frame_host->GetParent())
841 return;
843 if (name_ == name)
844 return;
846 ReportFrameNameChange(name);
849 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
850 name_ = name;
851 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
852 args->SetString(webview::kName, name);
853 DispatchEventToView(
854 new GuestViewEvent(webview::kEventFrameNameChanged, args.Pass()));
857 void WebViewGuest::LoadHandlerCalled() {
858 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
859 DispatchEventToView(
860 new GuestViewEvent(webview::kEventContentLoad, args.Pass()));
863 void WebViewGuest::LoadRedirect(const GURL& old_url,
864 const GURL& new_url,
865 bool is_top_level) {
866 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
867 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
868 args->SetString(webview::kNewURL, new_url.spec());
869 args->SetString(webview::kOldURL, old_url.spec());
870 DispatchEventToView(
871 new GuestViewEvent(webview::kEventLoadRedirect, args.Pass()));
874 void WebViewGuest::PushWebViewStateToIOThread() {
875 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
876 std::string partition_domain;
877 std::string partition_id;
878 bool in_memory;
879 if (!GetGuestPartitionConfigForSite(
880 site_url, &partition_domain, &partition_id, &in_memory)) {
881 NOTREACHED();
882 return;
885 WebViewRendererState::WebViewInfo web_view_info;
886 web_view_info.embedder_process_id =
887 owner_web_contents()->GetRenderProcessHost()->GetID();
888 web_view_info.instance_id = view_instance_id();
889 web_view_info.partition_id = partition_id;
890 web_view_info.owner_host = owner_host();
891 web_view_info.rules_registry_id = rules_registry_id_;
893 // Get content scripts IDs added by the guest.
894 WebViewContentScriptManager* manager =
895 WebViewContentScriptManager::Get(browser_context());
896 DCHECK(manager);
897 web_view_info.content_script_ids = manager->GetContentScriptIDSet(
898 web_view_info.embedder_process_id, web_view_info.instance_id);
900 content::BrowserThread::PostTask(
901 content::BrowserThread::IO,
902 FROM_HERE,
903 base::Bind(&WebViewRendererState::AddGuest,
904 base::Unretained(WebViewRendererState::GetInstance()),
905 web_contents()->GetRenderProcessHost()->GetID(),
906 web_contents()->GetRoutingID(),
907 web_view_info));
910 // static
911 void WebViewGuest::RemoveWebViewStateFromIOThread(
912 WebContents* web_contents) {
913 content::BrowserThread::PostTask(
914 content::BrowserThread::IO, FROM_HERE,
915 base::Bind(
916 &WebViewRendererState::RemoveGuest,
917 base::Unretained(WebViewRendererState::GetInstance()),
918 web_contents->GetRenderProcessHost()->GetID(),
919 web_contents->GetRoutingID()));
922 void WebViewGuest::RequestMediaAccessPermission(
923 WebContents* source,
924 const content::MediaStreamRequest& request,
925 const content::MediaResponseCallback& callback) {
926 web_view_permission_helper_->RequestMediaAccessPermission(source,
927 request,
928 callback);
931 bool WebViewGuest::CheckMediaAccessPermission(WebContents* source,
932 const GURL& security_origin,
933 content::MediaStreamType type) {
934 return web_view_permission_helper_->CheckMediaAccessPermission(
935 source, security_origin, type);
938 void WebViewGuest::CanDownload(
939 const GURL& url,
940 const std::string& request_method,
941 const base::Callback<void(bool)>& callback) {
942 web_view_permission_helper_->CanDownload(url, request_method, callback);
945 void WebViewGuest::RequestPointerLockPermission(
946 bool user_gesture,
947 bool last_unlocked_by_target,
948 const base::Callback<void(bool)>& callback) {
949 web_view_permission_helper_->RequestPointerLockPermission(
950 user_gesture,
951 last_unlocked_by_target,
952 callback);
955 void WebViewGuest::SignalWhenReady(const base::Closure& callback) {
956 auto manager = WebViewContentScriptManager::Get(browser_context());
957 manager->SignalOnScriptsLoaded(callback);
960 bool WebViewGuest::ShouldHandleFindRequestsForEmbedder() const {
961 if (web_view_guest_delegate_)
962 return web_view_guest_delegate_->ShouldHandleFindRequestsForEmbedder();
963 return false;
966 void WebViewGuest::WillAttachToEmbedder() {
967 rules_registry_id_ = GetOrGenerateRulesRegistryID(
968 owner_web_contents()->GetRenderProcessHost()->GetID(),
969 view_instance_id());
971 // We must install the mapping from guests to WebViews prior to resuming
972 // suspended resource loads so that the WebRequest API will catch resource
973 // requests.
974 PushWebViewStateToIOThread();
977 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
978 WebContents* source) {
979 return &javascript_dialog_helper_;
982 void WebViewGuest::NavigateGuest(const std::string& src,
983 bool force_navigation) {
984 if (src.empty())
985 return;
987 GURL url = ResolveURL(src);
989 // We wait for all the content scripts to load and then navigate the guest
990 // if the navigation is embedder-initiated. For browser-initiated navigations,
991 // content scripts will be ready.
992 if (force_navigation) {
993 SignalWhenReady(base::Bind(
994 &WebViewGuest::LoadURLWithParams, weak_ptr_factory_.GetWeakPtr(), url,
995 content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
996 GlobalRequestID(), force_navigation));
997 return;
999 LoadURLWithParams(url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1000 GlobalRequestID(), force_navigation);
1003 bool WebViewGuest::HandleKeyboardShortcuts(
1004 const content::NativeWebKeyboardEvent& event) {
1005 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
1006 if (!GuestViewManager::FromBrowserContext(browser_context())->
1007 IsOwnedByExtension(this)) {
1008 return false;
1011 if (event.type != blink::WebInputEvent::RawKeyDown)
1012 return false;
1014 // If the user hits the escape key without any modifiers then unlock the
1015 // mouse if necessary.
1016 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
1017 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
1018 return web_contents()->GotResponseToLockMouseRequest(false);
1021 #if defined(OS_MACOSX)
1022 if (event.modifiers != blink::WebInputEvent::MetaKey)
1023 return false;
1025 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
1026 Go(-1);
1027 return true;
1030 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
1031 Go(1);
1032 return true;
1034 #else
1035 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
1036 Go(-1);
1037 return true;
1040 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
1041 Go(1);
1042 return true;
1044 #endif
1046 return false;
1049 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
1050 std::string name;
1051 if (params.GetString(webview::kAttributeName, &name)) {
1052 // If the guest window's name is empty, then the WebView tag's name is
1053 // assigned. Otherwise, the guest window's name takes precedence over the
1054 // WebView tag's name.
1055 if (name_.empty())
1056 SetName(name);
1058 if (attached())
1059 ReportFrameNameChange(name_);
1061 std::string user_agent_override;
1062 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
1063 SetUserAgentOverride(user_agent_override);
1065 bool allow_transparency = false;
1066 if (params.GetBoolean(webview::kAttributeAllowTransparency,
1067 &allow_transparency)) {
1068 // We need to set the background opaque flag after navigation to ensure that
1069 // there is a RenderWidgetHostView available.
1070 SetAllowTransparency(allow_transparency);
1073 bool allow_scaling = false;
1074 if (params.GetBoolean(webview::kAttributeAllowScaling, &allow_scaling))
1075 SetAllowScaling(allow_scaling);
1077 // Check for a pending zoom from before the first navigation.
1078 params.GetDouble(webview::kInitialZoomFactor, &pending_zoom_factor_);
1080 bool is_pending_new_window = false;
1081 if (GetOpener()) {
1082 // We need to do a navigation here if the target URL has changed between
1083 // the time the WebContents was created and the time it was attached.
1084 // We also need to do an initial navigation if a RenderView was never
1085 // created for the new window in cases where there is no referrer.
1086 auto it = GetOpener()->pending_new_windows_.find(this);
1087 if (it != GetOpener()->pending_new_windows_.end()) {
1088 const NewWindowInfo& new_window_info = it->second;
1089 if (new_window_info.changed || !web_contents()->HasOpener())
1090 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
1092 // Once a new guest is attached to the DOM of the embedder page, then the
1093 // lifetime of the new guest is no longer managed by the opener guest.
1094 GetOpener()->pending_new_windows_.erase(this);
1096 is_pending_new_window = true;
1100 // Only read the src attribute if this is not a New Window API flow.
1101 if (!is_pending_new_window) {
1102 std::string src;
1103 if (params.GetString(webview::kAttributeSrc, &src))
1104 NavigateGuest(src, true /* force_navigation */);
1108 void WebViewGuest::ShowContextMenu(
1109 int request_id,
1110 const WebViewGuestDelegate::MenuItemVector* items) {
1111 if (web_view_guest_delegate_)
1112 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1115 void WebViewGuest::SetName(const std::string& name) {
1116 if (name_ == name)
1117 return;
1118 name_ = name;
1120 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1123 void WebViewGuest::SetZoom(double zoom_factor) {
1124 auto zoom_controller = ZoomController::FromWebContents(web_contents());
1125 DCHECK(zoom_controller);
1126 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1127 zoom_controller->SetZoomLevel(zoom_level);
1130 void WebViewGuest::SetZoomMode(ZoomController::ZoomMode zoom_mode) {
1131 ZoomController::FromWebContents(web_contents())->SetZoomMode(zoom_mode);
1134 void WebViewGuest::SetAllowTransparency(bool allow) {
1135 if (allow_transparency_ == allow)
1136 return;
1138 allow_transparency_ = allow;
1139 if (!web_contents()->GetRenderViewHost()->GetView())
1140 return;
1142 if (allow_transparency_) {
1143 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1144 SK_ColorTRANSPARENT);
1145 } else {
1146 web_contents()
1147 ->GetRenderViewHost()
1148 ->GetView()
1149 ->SetBackgroundColorToDefault();
1153 void WebViewGuest::SetAllowScaling(bool allow) {
1154 allow_scaling_ = allow;
1157 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1158 const std::string& base_url,
1159 const std::string& virtual_url,
1160 std::string* error) {
1161 // Make GURLs from URLs.
1162 const GURL data_gurl = GURL(data_url);
1163 const GURL base_gurl = GURL(base_url);
1164 const GURL virtual_gurl = GURL(virtual_url);
1166 // Check that the provided URLs are valid.
1167 // |data_url| must be a valid data URL.
1168 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1169 base::SStringPrintf(
1170 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1171 return false;
1173 // |base_url| must be a valid URL.
1174 if (!base_gurl.is_valid()) {
1175 base::SStringPrintf(
1176 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1177 return false;
1179 // |virtual_url| must be a valid URL.
1180 if (!virtual_gurl.is_valid()) {
1181 base::SStringPrintf(
1182 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1183 return false;
1186 // Set up the parameters to load |data_url| with the specified |base_url|.
1187 content::NavigationController::LoadURLParams load_params(data_gurl);
1188 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1189 load_params.base_url_for_data_url = base_gurl;
1190 load_params.virtual_url_for_data_url = virtual_gurl;
1191 load_params.override_user_agent =
1192 content::NavigationController::UA_OVERRIDE_INHERIT;
1194 // Navigate to the data URL.
1195 GuestViewBase::LoadURLWithParams(load_params);
1197 return true;
1200 void WebViewGuest::AddNewContents(WebContents* source,
1201 WebContents* new_contents,
1202 WindowOpenDisposition disposition,
1203 const gfx::Rect& initial_rect,
1204 bool user_gesture,
1205 bool* was_blocked) {
1206 if (was_blocked)
1207 *was_blocked = false;
1208 RequestNewWindowPermission(disposition,
1209 initial_rect,
1210 user_gesture,
1211 new_contents);
1214 WebContents* WebViewGuest::OpenURLFromTab(
1215 WebContents* source,
1216 const content::OpenURLParams& params) {
1217 // Most navigations should be handled by WebViewGuest::LoadURLWithParams,
1218 // which takes care of blocking chrome:// URLs and other web-unsafe schemes.
1219 // (NavigateGuest and CreateNewGuestWebViewWindow also go through
1220 // LoadURLWithParams.)
1222 // We make an exception here for context menu items, since the Language
1223 // Settings item uses a browser-initiated navigation to a chrome:// URL.
1224 // These can be passed to the embedder's WebContentsDelegate so that the
1225 // browser performs the action for the <webview>.
1226 if (!params.is_renderer_initiated &&
1227 !content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1228 params.url.scheme())) {
1229 if (!owner_web_contents()->GetDelegate())
1230 return nullptr;
1231 return owner_web_contents()->GetDelegate()->OpenURLFromTab(
1232 owner_web_contents(), params);
1235 if (!attached()) {
1236 WebViewGuest* opener = GetOpener();
1237 // If the guest wishes to navigate away prior to attachment then we save the
1238 // navigation to perform upon attachment. Navigation initializes a lot of
1239 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1240 // Navigation also resumes resource loading. If we were created using
1241 // newwindow (i.e. we have an opener), we don't allow navigation until
1242 // attachment.
1243 if (opener) {
1244 auto it = opener->pending_new_windows_.find(this);
1245 if (it == opener->pending_new_windows_.end())
1246 return nullptr;
1247 const NewWindowInfo& info = it->second;
1248 NewWindowInfo new_window_info(params.url, info.name);
1249 new_window_info.changed = new_window_info.url != info.url;
1250 it->second = new_window_info;
1251 return nullptr;
1255 // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
1256 // decides that a fork should happen. At the time of writing this comment,
1257 // the only way a well behaving guest could hit this code path is if it
1258 // navigates to a URL that's associated with the default search engine.
1259 // This list of URLs is generated by search::GetSearchURLs. Validity checks
1260 // are performed inside LoadURLWithParams such that if the guest attempts
1261 // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
1262 // event will fire in the embedder, and the guest will be navigated to
1263 // about:blank.
1264 if (params.disposition == CURRENT_TAB) {
1265 LoadURLWithParams(params.url, params.referrer, params.transition,
1266 params.transferred_global_request_id,
1267 true /* force_navigation */);
1268 return web_contents();
1271 // This code path is taken if Ctrl+Click, middle click or any of the
1272 // keyboard/mouse combinations are used to open a link in a new tab/window.
1273 // This code path is also taken on client-side redirects from about:blank.
1274 CreateNewGuestWebViewWindow(params);
1275 return nullptr;
1278 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1279 int opener_render_frame_id,
1280 const std::string& frame_name,
1281 const GURL& target_url,
1282 WebContents* new_contents) {
1283 auto guest = WebViewGuest::FromWebContents(new_contents);
1284 CHECK(guest);
1285 guest->SetOpener(this);
1286 guest->name_ = frame_name;
1287 pending_new_windows_.insert(
1288 std::make_pair(guest, NewWindowInfo(target_url, frame_name)));
1291 void WebViewGuest::EnterFullscreenModeForTab(WebContents* web_contents,
1292 const GURL& origin) {
1293 // Ask the embedder for permission.
1294 base::DictionaryValue request_info;
1295 request_info.SetString(webview::kOrigin, origin.spec());
1296 web_view_permission_helper_->RequestPermission(
1297 WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, request_info,
1298 base::Bind(&WebViewGuest::OnFullscreenPermissionDecided,
1299 weak_ptr_factory_.GetWeakPtr()),
1300 false /* allowed_by_default */);
1302 // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
1303 // bounds. If the embedder denies the permission then we will see a flicker.
1304 // Once we have the ability to "cancel" a renderer/ fullscreen request:
1305 // http://crbug.com/466854 this won't be necessary and we should be
1306 // Calling SetFullscreenState(true) once the embedder allowed the request.
1307 // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
1308 SetFullscreenState(true);
1311 void WebViewGuest::ExitFullscreenModeForTab(WebContents* web_contents) {
1312 SetFullscreenState(false);
1315 bool WebViewGuest::IsFullscreenForTabOrPending(
1316 const WebContents* web_contents) const {
1317 return is_guest_fullscreen_;
1320 void WebViewGuest::LoadURLWithParams(
1321 const GURL& url,
1322 const content::Referrer& referrer,
1323 ui::PageTransition transition_type,
1324 const GlobalRequestID& transferred_global_request_id,
1325 bool force_navigation) {
1326 if (!url.is_valid()) {
1327 LoadAbort(true /* is_top_level */, url, net::ERR_INVALID_URL,
1328 net::ErrorToShortString(net::ERR_INVALID_URL));
1329 NavigateGuest(url::kAboutBlankURL, false /* force_navigation */);
1330 return;
1333 bool scheme_is_blocked =
1334 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1335 url.scheme()) &&
1336 !url.SchemeIs(url::kAboutScheme)) ||
1337 url.SchemeIs(url::kJavaScriptScheme);
1339 // Do not allow navigating a guest to schemes other than known safe schemes.
1340 // This will block the embedder trying to load unwanted schemes, e.g.
1341 // chrome://.
1342 if (scheme_is_blocked) {
1343 LoadAbort(true /* is_top_level */, url, net::ERR_DISALLOWED_URL_SCHEME,
1344 net::ErrorToShortString(net::ERR_DISALLOWED_URL_SCHEME));
1345 NavigateGuest(url::kAboutBlankURL, false /* force_navigation */);
1346 return;
1349 if (!force_navigation && (src_ == url))
1350 return;
1352 GURL validated_url(url);
1353 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
1354 // As guests do not swap processes on navigation, only navigations to
1355 // normal web URLs are supported. No protocol handlers are installed for
1356 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
1357 // can be granted to the guest process.
1358 content::NavigationController::LoadURLParams load_url_params(validated_url);
1359 load_url_params.referrer = referrer;
1360 load_url_params.transition_type = transition_type;
1361 load_url_params.extra_headers = std::string();
1362 load_url_params.transferred_global_request_id = transferred_global_request_id;
1363 if (is_overriding_user_agent_) {
1364 load_url_params.override_user_agent =
1365 content::NavigationController::UA_OVERRIDE_TRUE;
1367 GuestViewBase::LoadURLWithParams(load_url_params);
1369 src_ = validated_url;
1372 void WebViewGuest::RequestNewWindowPermission(WindowOpenDisposition disposition,
1373 const gfx::Rect& initial_bounds,
1374 bool user_gesture,
1375 WebContents* new_contents) {
1376 auto guest = WebViewGuest::FromWebContents(new_contents);
1377 if (!guest)
1378 return;
1379 auto it = pending_new_windows_.find(guest);
1380 if (it == pending_new_windows_.end())
1381 return;
1382 const NewWindowInfo& new_window_info = it->second;
1384 // Retrieve the opener partition info if we have it.
1385 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1386 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1388 base::DictionaryValue request_info;
1389 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1390 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1391 request_info.Set(webview::kTargetURL,
1392 new base::StringValue(new_window_info.url.spec()));
1393 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1394 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1395 // We pass in partition info so that window-s created through newwindow
1396 // API can use it to set their partition attribute.
1397 request_info.Set(webview::kStoragePartitionId,
1398 new base::StringValue(storage_partition_id));
1399 request_info.Set(
1400 webview::kWindowOpenDisposition,
1401 new base::StringValue(WindowOpenDispositionToString(disposition)));
1403 web_view_permission_helper_->
1404 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1405 request_info,
1406 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1407 weak_ptr_factory_.GetWeakPtr(),
1408 guest->guest_instance_id()),
1409 false /* allowed_by_default */);
1412 GURL WebViewGuest::ResolveURL(const std::string& src) {
1413 if (!GuestViewManager::FromBrowserContext(browser_context())->
1414 IsOwnedByExtension(this)) {
1415 return GURL(src);
1418 GURL default_url(base::StringPrintf("%s://%s/",
1419 kExtensionScheme,
1420 owner_host().c_str()));
1421 return default_url.Resolve(src);
1424 void WebViewGuest::OnWebViewNewWindowResponse(
1425 int new_window_instance_id,
1426 bool allow,
1427 const std::string& user_input) {
1428 auto guest =
1429 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1430 new_window_instance_id);
1431 if (!guest)
1432 return;
1434 if (!allow)
1435 guest->Destroy();
1438 void WebViewGuest::OnFullscreenPermissionDecided(
1439 bool allowed,
1440 const std::string& user_input) {
1441 last_fullscreen_permission_was_allowed_by_embedder_ = allowed;
1442 SetFullscreenState(allowed);
1445 bool WebViewGuest::GuestMadeEmbedderFullscreen() const {
1446 return last_fullscreen_permission_was_allowed_by_embedder_ &&
1447 is_embedder_fullscreen_;
1450 void WebViewGuest::SetFullscreenState(bool is_fullscreen) {
1451 if (is_fullscreen == is_guest_fullscreen_)
1452 return;
1454 bool was_fullscreen = is_guest_fullscreen_;
1455 is_guest_fullscreen_ = is_fullscreen;
1456 // If the embedder entered fullscreen because of us, it should exit fullscreen
1457 // when we exit fullscreen.
1458 if (was_fullscreen && GuestMadeEmbedderFullscreen()) {
1459 // Dispatch a message so we can call document.webkitCancelFullscreen()
1460 // on the embedder.
1461 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1462 DispatchEventToView(
1463 new GuestViewEvent(webview::kEventExitFullscreen, args.Pass()));
1465 // Since we changed fullscreen state, sending a Resize message ensures that
1466 // renderer/ sees the change.
1467 web_contents()->GetRenderViewHost()->WasResized();
1470 } // namespace extensions