Remove linux_chromium_gn_dbg from the chromium CQ.
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blobe96b30e532beb8dc7dddfef4a67ba54adff6b76f
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/common/manifest_constants.h"
52 #include "extensions/strings/grit/extensions_strings.h"
53 #include "ipc/ipc_message_macros.h"
54 #include "net/base/escape.h"
55 #include "net/base/net_errors.h"
56 #include "ui/base/models/simple_menu_model.h"
57 #include "url/url_constants.h"
59 using base::UserMetricsAction;
60 using content::GlobalRequestID;
61 using content::RenderFrameHost;
62 using content::RenderProcessHost;
63 using content::ResourceType;
64 using content::StoragePartition;
65 using content::WebContents;
66 using guest_view::GuestViewBase;
67 using guest_view::GuestViewEvent;
68 using guest_view::GuestViewManager;
69 using ui_zoom::ZoomController;
71 namespace extensions {
73 namespace {
75 // Returns storage partition removal mask from web_view clearData mask. Note
76 // that storage partition mask is a subset of webview's data removal mask.
77 uint32 GetStoragePartitionRemovalMask(uint32 web_view_removal_mask) {
78 uint32 mask = 0;
79 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_APPCACHE)
80 mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
81 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_COOKIES)
82 mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
83 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_FILE_SYSTEMS)
84 mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
85 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_INDEXEDDB)
86 mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
87 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_LOCAL_STORAGE)
88 mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
89 if (web_view_removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_WEBSQL)
90 mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
92 return mask;
95 std::string WindowOpenDispositionToString(
96 WindowOpenDisposition window_open_disposition) {
97 switch (window_open_disposition) {
98 case IGNORE_ACTION:
99 return "ignore";
100 case SAVE_TO_DISK:
101 return "save_to_disk";
102 case CURRENT_TAB:
103 return "current_tab";
104 case NEW_BACKGROUND_TAB:
105 return "new_background_tab";
106 case NEW_FOREGROUND_TAB:
107 return "new_foreground_tab";
108 case NEW_WINDOW:
109 return "new_window";
110 case NEW_POPUP:
111 return "new_popup";
112 default:
113 NOTREACHED() << "Unknown Window Open Disposition";
114 return "ignore";
118 static std::string TerminationStatusToString(base::TerminationStatus status) {
119 switch (status) {
120 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
121 return "normal";
122 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
123 case base::TERMINATION_STATUS_STILL_RUNNING:
124 return "abnormal";
125 #if defined(OS_CHROMEOS)
126 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
127 return "oom killed";
128 #endif
129 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
130 return "killed";
131 case base::TERMINATION_STATUS_PROCESS_CRASHED:
132 return "crashed";
133 case base::TERMINATION_STATUS_LAUNCH_FAILED:
134 return "failed to launch";
135 case base::TERMINATION_STATUS_MAX_ENUM:
136 break;
138 NOTREACHED() << "Unknown Termination Status.";
139 return "unknown";
142 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
143 const std::string& partition_id = site_url.query();
144 bool persist_storage = site_url.path().find("persist") != std::string::npos;
145 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
148 void ParsePartitionParam(const base::DictionaryValue& create_params,
149 std::string* storage_partition_id,
150 bool* persist_storage) {
151 std::string partition_str;
152 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
153 return;
156 // Since the "persist:" prefix is in ASCII, base::StartsWith will work fine on
157 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
158 // remove the prefix without splicing in the middle of a multi-byte codepoint.
159 // We can use the rest of the string as UTF-8 encoded one.
160 if (base::StartsWith(partition_str, "persist:",
161 base::CompareCase::SENSITIVE)) {
162 size_t index = partition_str.find(":");
163 CHECK(index != std::string::npos);
164 // It is safe to do index + 1, since we tested for the full prefix above.
165 *storage_partition_id = partition_str.substr(index + 1);
167 if (storage_partition_id->empty()) {
168 // TODO(lazyboy): Better way to deal with this error.
169 return;
171 *persist_storage = true;
172 } else {
173 *storage_partition_id = partition_str;
174 *persist_storage = false;
178 void RemoveWebViewEventListenersOnIOThread(
179 void* profile,
180 int embedder_process_id,
181 int view_instance_id) {
182 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
183 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
184 profile,
185 embedder_process_id,
186 view_instance_id);
189 double ConvertZoomLevelToZoomFactor(double zoom_level) {
190 double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level);
191 // Because the conversion from zoom level to zoom factor isn't perfect, the
192 // resulting zoom factor is rounded to the nearest 6th decimal place.
193 zoom_factor = round(zoom_factor * 1000000) / 1000000;
194 return zoom_factor;
197 using WebViewKey = std::pair<int, int>;
198 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
199 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
200 LAZY_INSTANCE_INITIALIZER;
202 } // namespace
204 // static
205 void WebViewGuest::CleanUp(content::BrowserContext* browser_context,
206 int embedder_process_id,
207 int view_instance_id) {
208 GuestViewBase::CleanUp(browser_context, embedder_process_id,
209 view_instance_id);
211 // Clean up rules registries for the WebView.
212 WebViewKey key(embedder_process_id, view_instance_id);
213 auto it = web_view_key_to_id_map.Get().find(key);
214 if (it != web_view_key_to_id_map.Get().end()) {
215 auto rules_registry_id = it->second;
216 web_view_key_to_id_map.Get().erase(it);
217 RulesRegistryService* rrs =
218 RulesRegistryService::GetIfExists(browser_context);
219 if (rrs)
220 rrs->RemoveRulesRegistriesByID(rules_registry_id);
223 // Clean up web request event listeners for the WebView.
224 content::BrowserThread::PostTask(
225 content::BrowserThread::IO,
226 FROM_HERE,
227 base::Bind(
228 &RemoveWebViewEventListenersOnIOThread,
229 browser_context,
230 embedder_process_id,
231 view_instance_id));
233 // Clean up content scripts for the WebView.
234 auto csm = WebViewContentScriptManager::Get(browser_context);
235 csm->RemoveAllContentScriptsForWebView(embedder_process_id, view_instance_id);
237 // Allow an extensions browser client to potentially perform more cleanup.
238 ExtensionsBrowserClient::Get()->CleanUpWebView(
239 browser_context, embedder_process_id, view_instance_id);
242 // static
243 GuestViewBase* WebViewGuest::Create(WebContents* owner_web_contents) {
244 return new WebViewGuest(owner_web_contents);
247 // static
248 bool WebViewGuest::GetGuestPartitionConfigForSite(
249 const GURL& site,
250 std::string* partition_domain,
251 std::string* partition_name,
252 bool* in_memory) {
253 if (!site.SchemeIs(content::kGuestScheme))
254 return false;
256 // Since guest URLs are only used for packaged apps, there must be an app
257 // id in the URL.
258 CHECK(site.has_host());
259 *partition_domain = site.host();
260 // Since persistence is optional, the path must either be empty or the
261 // literal string.
262 *in_memory = (site.path() != "/persist");
263 // The partition name is user supplied value, which we have encoded when the
264 // URL was created, so it needs to be decoded.
265 *partition_name =
266 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
267 return true;
270 // static
271 std::string WebViewGuest::GetPartitionID(
272 const RenderProcessHost* render_process_host) {
273 WebViewRendererState* renderer_state = WebViewRendererState::GetInstance();
274 int process_id = render_process_host->GetID();
275 std::string partition_id;
276 if (renderer_state->IsGuest(process_id))
277 renderer_state->GetPartitionID(process_id, &partition_id);
279 return partition_id;
282 // static
283 const char WebViewGuest::Type[] = "webview";
285 // static
286 int WebViewGuest::GetOrGenerateRulesRegistryID(
287 int embedder_process_id,
288 int webview_instance_id) {
289 bool is_web_view = embedder_process_id && webview_instance_id;
290 if (!is_web_view)
291 return RulesRegistryService::kDefaultRulesRegistryID;
293 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
294 auto it = web_view_key_to_id_map.Get().find(key);
295 if (it != web_view_key_to_id_map.Get().end())
296 return it->second;
298 auto rph = RenderProcessHost::FromID(embedder_process_id);
299 int rules_registry_id =
300 RulesRegistryService::Get(rph->GetBrowserContext())->
301 GetNextRulesRegistryID();
302 web_view_key_to_id_map.Get()[key] = rules_registry_id;
303 return rules_registry_id;
306 bool WebViewGuest::CanRunInDetachedState() const {
307 return true;
310 void WebViewGuest::CreateWebContents(
311 const base::DictionaryValue& create_params,
312 const WebContentsCreatedCallback& callback) {
313 RenderProcessHost* owner_render_process_host =
314 owner_web_contents()->GetRenderProcessHost();
315 std::string storage_partition_id;
316 bool persist_storage = false;
317 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
318 // Validate that the partition id coming from the renderer is valid UTF-8,
319 // since we depend on this in other parts of the code, such as FilePath
320 // creation. If the validation fails, treat it as a bad message and kill the
321 // renderer process.
322 if (!base::IsStringUTF8(storage_partition_id)) {
323 content::RecordAction(
324 base::UserMetricsAction("BadMessageTerminate_BPGM"));
325 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
326 false);
327 callback.Run(nullptr);
328 return;
330 std::string url_encoded_partition = net::EscapeQueryParamValue(
331 storage_partition_id, false);
332 std::string partition_domain = GetOwnerSiteURL().host();
333 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
334 content::kGuestScheme,
335 partition_domain.c_str(),
336 persist_storage ? "persist" : "",
337 url_encoded_partition.c_str()));
339 // If we already have a webview tag in the same app using the same storage
340 // partition, we should use the same SiteInstance so the existing tag and
341 // the new tag can script each other.
342 auto guest_view_manager = GuestViewManager::FromBrowserContext(
343 owner_render_process_host->GetBrowserContext());
344 content::SiteInstance* guest_site_instance =
345 guest_view_manager->GetGuestSiteInstance(guest_site);
346 if (!guest_site_instance) {
347 // Create the SiteInstance in a new BrowsingInstance, which will ensure
348 // that webview tags are also not allowed to send messages across
349 // different partitions.
350 guest_site_instance = content::SiteInstance::CreateForURL(
351 owner_render_process_host->GetBrowserContext(), guest_site);
353 WebContents::CreateParams params(
354 owner_render_process_host->GetBrowserContext(),
355 guest_site_instance);
356 params.guest_delegate = this;
357 callback.Run(WebContents::Create(params));
360 void WebViewGuest::DidAttachToEmbedder() {
361 ApplyAttributes(*attach_params());
364 void WebViewGuest::DidDropLink(const GURL& url) {
365 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
366 args->SetString(guest_view::kUrl, url.spec());
367 DispatchEventToView(
368 new GuestViewEvent(webview::kEventDropLink, args.Pass()));
371 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
372 script_executor_.reset(
373 new ScriptExecutor(web_contents(), &script_observers_));
375 notification_registrar_.Add(this,
376 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
377 content::Source<WebContents>(web_contents()));
379 notification_registrar_.Add(this,
380 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
381 content::Source<WebContents>(web_contents()));
383 if (web_view_guest_delegate_)
384 web_view_guest_delegate_->OnDidInitialize();
385 ExtensionsAPIClient::Get()->AttachWebContentsHelpers(web_contents());
386 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
388 rules_registry_id_ = GetOrGenerateRulesRegistryID(
389 owner_web_contents()->GetRenderProcessHost()->GetID(),
390 view_instance_id());
392 // We must install the mapping from guests to WebViews prior to resuming
393 // suspended resource loads so that the WebRequest API will catch resource
394 // requests.
395 PushWebViewStateToIOThread();
397 ApplyAttributes(create_params);
400 void WebViewGuest::ClearDataInternal(base::Time remove_since,
401 uint32 removal_mask,
402 const base::Closure& callback) {
403 uint32 storage_partition_removal_mask =
404 GetStoragePartitionRemovalMask(removal_mask);
405 if (!storage_partition_removal_mask) {
406 callback.Run();
407 return;
409 content::StoragePartition* partition =
410 content::BrowserContext::GetStoragePartition(
411 web_contents()->GetBrowserContext(),
412 web_contents()->GetSiteInstance());
413 partition->ClearData(
414 storage_partition_removal_mask,
415 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, GURL(),
416 content::StoragePartition::OriginMatcherFunction(), remove_since,
417 base::Time::Now(), callback);
420 void WebViewGuest::GuestViewDidStopLoading() {
421 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
422 DispatchEventToView(
423 new GuestViewEvent(webview::kEventLoadStop, args.Pass()));
426 void WebViewGuest::EmbedderFullscreenToggled(bool entered_fullscreen) {
427 is_embedder_fullscreen_ = entered_fullscreen;
428 // If the embedder has got out of fullscreen, we get out of fullscreen
429 // mode as well.
430 if (!entered_fullscreen)
431 SetFullscreenState(false);
434 const char* WebViewGuest::GetAPINamespace() const {
435 return webview::kAPINamespace;
438 int WebViewGuest::GetTaskPrefix() const {
439 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
442 void WebViewGuest::GuestDestroyed() {
443 RemoveWebViewStateFromIOThread(web_contents());
446 void WebViewGuest::GuestReady() {
447 // The guest RenderView should always live in an isolated guest process.
448 CHECK(web_contents()->GetRenderProcessHost()->IsForGuestsOnly());
449 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
451 // We don't want to accidentally set the opacity of an interstitial page.
452 // WebContents::GetRenderWidgetHostView will return the RWHV of an
453 // interstitial page if one is showing at this time. We only want opacity
454 // to apply to web pages.
455 if (allow_transparency_) {
456 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
457 SK_ColorTRANSPARENT);
458 } else {
459 web_contents()
460 ->GetRenderViewHost()
461 ->GetView()
462 ->SetBackgroundColorToDefault();
466 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
467 const gfx::Size& new_size) {
468 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
469 args->SetInteger(webview::kOldHeight, old_size.height());
470 args->SetInteger(webview::kOldWidth, old_size.width());
471 args->SetInteger(webview::kNewHeight, new_size.height());
472 args->SetInteger(webview::kNewWidth, new_size.width());
473 DispatchEventToView(
474 new GuestViewEvent(webview::kEventSizeChanged, args.Pass()));
477 bool WebViewGuest::IsAutoSizeSupported() const {
478 return true;
481 void WebViewGuest::GuestZoomChanged(double old_zoom_level,
482 double new_zoom_level) {
483 // Dispatch the zoomchange event.
484 double old_zoom_factor = ConvertZoomLevelToZoomFactor(old_zoom_level);
485 double new_zoom_factor = ConvertZoomLevelToZoomFactor(new_zoom_level);
486 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
487 args->SetDouble(webview::kOldZoomFactor, old_zoom_factor);
488 args->SetDouble(webview::kNewZoomFactor, new_zoom_factor);
489 DispatchEventToView(
490 new GuestViewEvent(webview::kEventZoomChange, args.Pass()));
493 void WebViewGuest::WillDestroy() {
494 if (!attached() && GetOpener())
495 GetOpener()->pending_new_windows_.erase(this);
498 bool WebViewGuest::AddMessageToConsole(WebContents* source,
499 int32 level,
500 const base::string16& message,
501 int32 line_no,
502 const base::string16& source_id) {
503 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
504 // Log levels are from base/logging.h: LogSeverity.
505 args->SetInteger(webview::kLevel, level);
506 args->SetString(webview::kMessage, message);
507 args->SetInteger(webview::kLine, line_no);
508 args->SetString(webview::kSourceId, source_id);
509 DispatchEventToView(
510 new GuestViewEvent(webview::kEventConsoleMessage, args.Pass()));
511 return true;
514 void WebViewGuest::CloseContents(WebContents* source) {
515 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
516 DispatchEventToView(
517 new GuestViewEvent(webview::kEventClose, args.Pass()));
520 void WebViewGuest::FindReply(WebContents* source,
521 int request_id,
522 int number_of_matches,
523 const gfx::Rect& selection_rect,
524 int active_match_ordinal,
525 bool final_update) {
526 GuestViewBase::FindReply(source, request_id, number_of_matches,
527 selection_rect, active_match_ordinal, final_update);
528 find_helper_.FindReply(request_id, number_of_matches, selection_rect,
529 active_match_ordinal, final_update);
532 double WebViewGuest::GetZoom() const {
533 double zoom_level =
534 ZoomController::FromWebContents(web_contents())->GetZoomLevel();
535 return ConvertZoomLevelToZoomFactor(zoom_level);
538 ZoomController::ZoomMode WebViewGuest::GetZoomMode() {
539 return ZoomController::FromWebContents(web_contents())->zoom_mode();
542 bool WebViewGuest::HandleContextMenu(
543 const content::ContextMenuParams& params) {
544 if (!web_view_guest_delegate_)
545 return false;
546 return web_view_guest_delegate_->HandleContextMenu(params);
549 void WebViewGuest::HandleKeyboardEvent(
550 WebContents* source,
551 const content::NativeWebKeyboardEvent& event) {
552 if (HandleKeyboardShortcuts(event))
553 return;
555 GuestViewBase::HandleKeyboardEvent(source, event);
558 bool WebViewGuest::PreHandleGestureEvent(WebContents* source,
559 const blink::WebGestureEvent& event) {
560 return !allow_scaling_ && GuestViewBase::PreHandleGestureEvent(source, event);
563 void WebViewGuest::LoadProgressChanged(WebContents* source, double progress) {
564 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
565 args->SetString(guest_view::kUrl, web_contents()->GetURL().spec());
566 args->SetDouble(webview::kProgress, progress);
567 DispatchEventToView(
568 new GuestViewEvent(webview::kEventLoadProgress, args.Pass()));
571 void WebViewGuest::LoadAbort(bool is_top_level,
572 const GURL& url,
573 int error_code,
574 const std::string& error_type) {
575 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
576 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
577 args->SetString(guest_view::kUrl, url.possibly_invalid_spec());
578 args->SetInteger(guest_view::kCode, error_code);
579 args->SetString(guest_view::kReason, error_type);
580 DispatchEventToView(
581 new GuestViewEvent(webview::kEventLoadAbort, args.Pass()));
584 void WebViewGuest::SetContextMenuPosition(const gfx::Point& position) {
585 if (web_view_guest_delegate_)
586 web_view_guest_delegate_->SetContextMenuPosition(position);
589 void WebViewGuest::CreateNewGuestWebViewWindow(
590 const content::OpenURLParams& params) {
591 GuestViewManager* guest_manager =
592 GuestViewManager::FromBrowserContext(browser_context());
593 // Set the attach params to use the same partition as the opener.
594 // We pull the partition information from the site's URL, which is of the
595 // form guest://site/{persist}?{partition_name}.
596 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
597 const std::string storage_partition_id =
598 GetStoragePartitionIdFromSiteURL(site_url);
599 base::DictionaryValue create_params;
600 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
602 guest_manager->CreateGuest(WebViewGuest::Type,
603 embedder_web_contents(),
604 create_params,
605 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
606 weak_ptr_factory_.GetWeakPtr(),
607 params));
610 void WebViewGuest::NewGuestWebViewCallback(const content::OpenURLParams& params,
611 WebContents* guest_web_contents) {
612 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
613 new_guest->SetOpener(this);
615 // Take ownership of |new_guest|.
616 pending_new_windows_.insert(
617 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
619 // Request permission to show the new window.
620 RequestNewWindowPermission(params.disposition,
621 gfx::Rect(),
622 params.user_gesture,
623 new_guest->web_contents());
626 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
627 // 'unresponsive' events.
628 void WebViewGuest::RendererResponsive(WebContents* source) {
629 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
630 args->SetInteger(webview::kProcessId,
631 web_contents()->GetRenderProcessHost()->GetID());
632 DispatchEventToView(
633 new GuestViewEvent(webview::kEventResponsive, args.Pass()));
636 void WebViewGuest::RendererUnresponsive(WebContents* source) {
637 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
638 args->SetInteger(webview::kProcessId,
639 web_contents()->GetRenderProcessHost()->GetID());
640 DispatchEventToView(
641 new GuestViewEvent(webview::kEventUnresponsive, args.Pass()));
644 void WebViewGuest::Observe(int type,
645 const content::NotificationSource& source,
646 const content::NotificationDetails& details) {
647 switch (type) {
648 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
649 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
650 if (content::Source<WebContents>(source).ptr() == web_contents())
651 LoadHandlerCalled();
652 break;
654 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
655 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
656 content::ResourceRedirectDetails* resource_redirect_details =
657 content::Details<content::ResourceRedirectDetails>(details).ptr();
658 bool is_top_level = resource_redirect_details->resource_type ==
659 content::RESOURCE_TYPE_MAIN_FRAME;
660 LoadRedirect(resource_redirect_details->url,
661 resource_redirect_details->new_url,
662 is_top_level);
663 break;
665 default:
666 NOTREACHED() << "Unexpected notification sent.";
667 break;
671 void WebViewGuest::StartFindInternal(
672 const base::string16& search_text,
673 const blink::WebFindOptions& options,
674 scoped_refptr<WebViewInternalFindFunction> find_function) {
675 find_helper_.Find(web_contents(), search_text, options, find_function);
678 void WebViewGuest::StopFindingInternal(content::StopFindAction action) {
679 find_helper_.CancelAllFindSessions();
680 web_contents()->StopFinding(action);
683 bool WebViewGuest::Go(int relative_index) {
684 content::NavigationController& controller = web_contents()->GetController();
685 if (!controller.CanGoToOffset(relative_index))
686 return false;
688 controller.GoToOffset(relative_index);
689 return true;
692 void WebViewGuest::Reload() {
693 // TODO(fsamuel): Don't check for repost because we don't want to show
694 // Chromium's repost warning. We might want to implement a separate API
695 // for registering a callback if a repost is about to happen.
696 web_contents()->GetController().Reload(false);
699 void WebViewGuest::SetUserAgentOverride(
700 const std::string& user_agent_override) {
701 is_overriding_user_agent_ = !user_agent_override.empty();
702 if (is_overriding_user_agent_) {
703 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
705 web_contents()->SetUserAgentOverride(user_agent_override);
708 void WebViewGuest::Stop() {
709 web_contents()->Stop();
712 void WebViewGuest::Terminate() {
713 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
714 base::ProcessHandle process_handle =
715 web_contents()->GetRenderProcessHost()->GetHandle();
716 if (process_handle)
717 web_contents()->GetRenderProcessHost()->Shutdown(
718 content::RESULT_CODE_KILLED, false);
721 bool WebViewGuest::ClearData(base::Time remove_since,
722 uint32 removal_mask,
723 const base::Closure& callback) {
724 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
725 content::StoragePartition* partition =
726 content::BrowserContext::GetStoragePartition(
727 web_contents()->GetBrowserContext(),
728 web_contents()->GetSiteInstance());
730 if (!partition)
731 return false;
733 if (removal_mask & webview::WEB_VIEW_REMOVE_DATA_MASK_CACHE) {
734 // First clear http cache data and then clear the rest in
735 // |ClearDataInternal|.
736 int render_process_id = web_contents()->GetRenderProcessHost()->GetID();
737 // We need to clear renderer cache separately for our process because
738 // StoragePartitionHttpCacheDataRemover::ClearData() does not clear that.
739 web_cache::WebCacheManager::GetInstance()->Remove(render_process_id);
740 web_cache::WebCacheManager::GetInstance()->ClearCacheForProcess(
741 render_process_id);
743 base::Closure cache_removal_done_callback = base::Bind(
744 &WebViewGuest::ClearDataInternal, weak_ptr_factory_.GetWeakPtr(),
745 remove_since, removal_mask, callback);
746 // StoragePartitionHttpCacheDataRemover removes itself when it is done.
747 // components/, move |ClearCache| to WebViewGuest: http//crbug.com/471287.
748 browsing_data::StoragePartitionHttpCacheDataRemover::CreateForRange(
749 partition, remove_since, base::Time::Now())
750 ->Remove(cache_removal_done_callback);
752 return true;
755 ClearDataInternal(remove_since, removal_mask, callback);
756 return true;
759 WebViewGuest::WebViewGuest(WebContents* owner_web_contents)
760 : GuestView<WebViewGuest>(owner_web_contents),
761 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
762 find_helper_(this),
763 is_overriding_user_agent_(false),
764 allow_transparency_(false),
765 javascript_dialog_helper_(this),
766 allow_scaling_(false),
767 is_guest_fullscreen_(false),
768 is_embedder_fullscreen_(false),
769 last_fullscreen_permission_was_allowed_by_embedder_(false),
770 pending_zoom_factor_(0.0),
771 weak_ptr_factory_(this) {
772 web_view_guest_delegate_.reset(
773 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
776 WebViewGuest::~WebViewGuest() {
779 void WebViewGuest::DidCommitProvisionalLoadForFrame(
780 content::RenderFrameHost* render_frame_host,
781 const GURL& url,
782 ui::PageTransition transition_type) {
783 if (!render_frame_host->GetParent()) {
784 src_ = url;
785 // Handle a pending zoom if one exists.
786 if (pending_zoom_factor_) {
787 SetZoom(pending_zoom_factor_);
788 pending_zoom_factor_ = 0.0;
791 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
792 args->SetString(guest_view::kUrl, url.spec());
793 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
794 args->SetString(webview::kInternalBaseURLForDataURL,
795 web_contents()
796 ->GetController()
797 .GetLastCommittedEntry()
798 ->GetBaseURLForDataURL()
799 .spec());
800 args->SetInteger(webview::kInternalCurrentEntryIndex,
801 web_contents()->GetController().GetCurrentEntryIndex());
802 args->SetInteger(webview::kInternalEntryCount,
803 web_contents()->GetController().GetEntryCount());
804 args->SetInteger(webview::kInternalProcessId,
805 web_contents()->GetRenderProcessHost()->GetID());
806 DispatchEventToView(
807 new GuestViewEvent(webview::kEventLoadCommit, args.Pass()));
809 find_helper_.CancelAllFindSessions();
812 void WebViewGuest::DidFailProvisionalLoad(
813 content::RenderFrameHost* render_frame_host,
814 const GURL& validated_url,
815 int error_code,
816 const base::string16& error_description,
817 bool was_ignored_by_handler) {
818 // Suppress loadabort for "mailto" URLs.
819 if (validated_url.SchemeIs(url::kMailToScheme))
820 return;
822 LoadAbort(!render_frame_host->GetParent(), validated_url, error_code,
823 net::ErrorToShortString(error_code));
826 void WebViewGuest::DidStartProvisionalLoadForFrame(
827 content::RenderFrameHost* render_frame_host,
828 const GURL& validated_url,
829 bool is_error_page,
830 bool is_iframe_srcdoc) {
831 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
832 args->SetString(guest_view::kUrl, validated_url.spec());
833 args->SetBoolean(guest_view::kIsTopLevel, !render_frame_host->GetParent());
834 DispatchEventToView(
835 new GuestViewEvent(webview::kEventLoadStart, args.Pass()));
838 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
839 // Cancel all find sessions in progress.
840 find_helper_.CancelAllFindSessions();
842 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
843 args->SetInteger(webview::kProcessId,
844 web_contents()->GetRenderProcessHost()->GetID());
845 args->SetString(webview::kReason, TerminationStatusToString(status));
846 DispatchEventToView(
847 new GuestViewEvent(webview::kEventExit, args.Pass()));
850 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
851 content::NavigationController& controller = web_contents()->GetController();
852 content::NavigationEntry* entry = controller.GetVisibleEntry();
853 if (!entry)
854 return;
855 entry->SetIsOverridingUserAgent(!user_agent.empty());
856 web_contents()->GetController().Reload(false);
859 void WebViewGuest::FrameNameChanged(RenderFrameHost* render_frame_host,
860 const std::string& name) {
861 if (render_frame_host->GetParent())
862 return;
864 if (name_ == name)
865 return;
867 ReportFrameNameChange(name);
870 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
871 name_ = name;
872 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
873 args->SetString(webview::kName, name);
874 DispatchEventToView(
875 new GuestViewEvent(webview::kEventFrameNameChanged, args.Pass()));
878 void WebViewGuest::LoadHandlerCalled() {
879 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
880 DispatchEventToView(
881 new GuestViewEvent(webview::kEventContentLoad, args.Pass()));
884 void WebViewGuest::LoadRedirect(const GURL& old_url,
885 const GURL& new_url,
886 bool is_top_level) {
887 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
888 args->SetBoolean(guest_view::kIsTopLevel, is_top_level);
889 args->SetString(webview::kNewURL, new_url.spec());
890 args->SetString(webview::kOldURL, old_url.spec());
891 DispatchEventToView(
892 new GuestViewEvent(webview::kEventLoadRedirect, args.Pass()));
895 void WebViewGuest::PushWebViewStateToIOThread() {
896 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
897 std::string partition_domain;
898 std::string partition_id;
899 bool in_memory;
900 if (!GetGuestPartitionConfigForSite(
901 site_url, &partition_domain, &partition_id, &in_memory)) {
902 NOTREACHED();
903 return;
906 WebViewRendererState::WebViewInfo web_view_info;
907 web_view_info.embedder_process_id =
908 owner_web_contents()->GetRenderProcessHost()->GetID();
909 web_view_info.instance_id = view_instance_id();
910 web_view_info.partition_id = partition_id;
911 web_view_info.owner_host = owner_host();
912 web_view_info.rules_registry_id = rules_registry_id_;
914 // Get content scripts IDs added by the guest.
915 WebViewContentScriptManager* manager =
916 WebViewContentScriptManager::Get(browser_context());
917 DCHECK(manager);
918 web_view_info.content_script_ids = manager->GetContentScriptIDSet(
919 web_view_info.embedder_process_id, web_view_info.instance_id);
921 content::BrowserThread::PostTask(
922 content::BrowserThread::IO,
923 FROM_HERE,
924 base::Bind(&WebViewRendererState::AddGuest,
925 base::Unretained(WebViewRendererState::GetInstance()),
926 web_contents()->GetRenderProcessHost()->GetID(),
927 web_contents()->GetRoutingID(),
928 web_view_info));
931 // static
932 void WebViewGuest::RemoveWebViewStateFromIOThread(
933 WebContents* web_contents) {
934 content::BrowserThread::PostTask(
935 content::BrowserThread::IO, FROM_HERE,
936 base::Bind(
937 &WebViewRendererState::RemoveGuest,
938 base::Unretained(WebViewRendererState::GetInstance()),
939 web_contents->GetRenderProcessHost()->GetID(),
940 web_contents->GetRoutingID()));
943 void WebViewGuest::RequestMediaAccessPermission(
944 WebContents* source,
945 const content::MediaStreamRequest& request,
946 const content::MediaResponseCallback& callback) {
947 web_view_permission_helper_->RequestMediaAccessPermission(source,
948 request,
949 callback);
952 bool WebViewGuest::CheckMediaAccessPermission(WebContents* source,
953 const GURL& security_origin,
954 content::MediaStreamType type) {
955 return web_view_permission_helper_->CheckMediaAccessPermission(
956 source, security_origin, type);
959 void WebViewGuest::CanDownload(
960 const GURL& url,
961 const std::string& request_method,
962 const base::Callback<void(bool)>& callback) {
963 web_view_permission_helper_->CanDownload(url, request_method, callback);
966 void WebViewGuest::RequestPointerLockPermission(
967 bool user_gesture,
968 bool last_unlocked_by_target,
969 const base::Callback<void(bool)>& callback) {
970 web_view_permission_helper_->RequestPointerLockPermission(
971 user_gesture,
972 last_unlocked_by_target,
973 callback);
976 void WebViewGuest::SignalWhenReady(const base::Closure& callback) {
977 auto manager = WebViewContentScriptManager::Get(browser_context());
978 manager->SignalOnScriptsLoaded(callback);
981 bool WebViewGuest::ShouldHandleFindRequestsForEmbedder() const {
982 if (web_view_guest_delegate_)
983 return web_view_guest_delegate_->ShouldHandleFindRequestsForEmbedder();
984 return false;
987 void WebViewGuest::WillAttachToEmbedder() {
988 rules_registry_id_ = GetOrGenerateRulesRegistryID(
989 owner_web_contents()->GetRenderProcessHost()->GetID(),
990 view_instance_id());
992 // We must install the mapping from guests to WebViews prior to resuming
993 // suspended resource loads so that the WebRequest API will catch resource
994 // requests.
995 PushWebViewStateToIOThread();
998 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
999 WebContents* source) {
1000 return &javascript_dialog_helper_;
1003 void WebViewGuest::NavigateGuest(const std::string& src,
1004 bool force_navigation) {
1005 if (src.empty())
1006 return;
1008 GURL url = ResolveURL(src);
1010 // We wait for all the content scripts to load and then navigate the guest
1011 // if the navigation is embedder-initiated. For browser-initiated navigations,
1012 // content scripts will be ready.
1013 if (force_navigation) {
1014 SignalWhenReady(base::Bind(
1015 &WebViewGuest::LoadURLWithParams, weak_ptr_factory_.GetWeakPtr(), url,
1016 content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1017 GlobalRequestID(), force_navigation));
1018 return;
1020 LoadURLWithParams(url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
1021 GlobalRequestID(), force_navigation);
1024 bool WebViewGuest::HandleKeyboardShortcuts(
1025 const content::NativeWebKeyboardEvent& event) {
1026 // <webview> outside of Chrome Apps do not handle keyboard shortcuts.
1027 if (!GuestViewManager::FromBrowserContext(browser_context())->
1028 IsOwnedByExtension(this)) {
1029 return false;
1032 if (event.type != blink::WebInputEvent::RawKeyDown)
1033 return false;
1035 // If the user hits the escape key without any modifiers then unlock the
1036 // mouse if necessary.
1037 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
1038 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
1039 return web_contents()->GotResponseToLockMouseRequest(false);
1042 #if defined(OS_MACOSX)
1043 if (event.modifiers != blink::WebInputEvent::MetaKey)
1044 return false;
1046 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
1047 Go(-1);
1048 return true;
1051 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
1052 Go(1);
1053 return true;
1055 #else
1056 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
1057 Go(-1);
1058 return true;
1061 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
1062 Go(1);
1063 return true;
1065 #endif
1067 return false;
1070 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
1071 std::string name;
1072 if (params.GetString(webview::kAttributeName, &name)) {
1073 // If the guest window's name is empty, then the WebView tag's name is
1074 // assigned. Otherwise, the guest window's name takes precedence over the
1075 // WebView tag's name.
1076 if (name_.empty())
1077 SetName(name);
1079 if (attached())
1080 ReportFrameNameChange(name_);
1082 std::string user_agent_override;
1083 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
1084 SetUserAgentOverride(user_agent_override);
1086 bool allow_transparency = false;
1087 if (params.GetBoolean(webview::kAttributeAllowTransparency,
1088 &allow_transparency)) {
1089 // We need to set the background opaque flag after navigation to ensure that
1090 // there is a RenderWidgetHostView available.
1091 SetAllowTransparency(allow_transparency);
1094 bool allow_scaling = false;
1095 if (params.GetBoolean(webview::kAttributeAllowScaling, &allow_scaling))
1096 SetAllowScaling(allow_scaling);
1098 // Check for a pending zoom from before the first navigation.
1099 params.GetDouble(webview::kInitialZoomFactor, &pending_zoom_factor_);
1101 bool is_pending_new_window = false;
1102 if (GetOpener()) {
1103 // We need to do a navigation here if the target URL has changed between
1104 // the time the WebContents was created and the time it was attached.
1105 // We also need to do an initial navigation if a RenderView was never
1106 // created for the new window in cases where there is no referrer.
1107 auto it = GetOpener()->pending_new_windows_.find(this);
1108 if (it != GetOpener()->pending_new_windows_.end()) {
1109 const NewWindowInfo& new_window_info = it->second;
1110 if (new_window_info.changed || !web_contents()->HasOpener())
1111 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
1113 // Once a new guest is attached to the DOM of the embedder page, then the
1114 // lifetime of the new guest is no longer managed by the opener guest.
1115 GetOpener()->pending_new_windows_.erase(this);
1117 is_pending_new_window = true;
1121 // Only read the src attribute if this is not a New Window API flow.
1122 if (!is_pending_new_window) {
1123 std::string src;
1124 if (params.GetString(webview::kAttributeSrc, &src))
1125 NavigateGuest(src, true /* force_navigation */);
1129 void WebViewGuest::ShowContextMenu(
1130 int request_id,
1131 const WebViewGuestDelegate::MenuItemVector* items) {
1132 if (web_view_guest_delegate_)
1133 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1136 void WebViewGuest::SetName(const std::string& name) {
1137 if (name_ == name)
1138 return;
1139 name_ = name;
1141 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1144 void WebViewGuest::SetZoom(double zoom_factor) {
1145 auto zoom_controller = ZoomController::FromWebContents(web_contents());
1146 DCHECK(zoom_controller);
1147 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1148 zoom_controller->SetZoomLevel(zoom_level);
1151 void WebViewGuest::SetZoomMode(ZoomController::ZoomMode zoom_mode) {
1152 ZoomController::FromWebContents(web_contents())->SetZoomMode(zoom_mode);
1155 void WebViewGuest::SetAllowTransparency(bool allow) {
1156 if (allow_transparency_ == allow)
1157 return;
1159 allow_transparency_ = allow;
1160 if (!web_contents()->GetRenderViewHost()->GetView())
1161 return;
1163 if (allow_transparency_) {
1164 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1165 SK_ColorTRANSPARENT);
1166 } else {
1167 web_contents()
1168 ->GetRenderViewHost()
1169 ->GetView()
1170 ->SetBackgroundColorToDefault();
1174 void WebViewGuest::SetAllowScaling(bool allow) {
1175 allow_scaling_ = allow;
1178 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1179 const std::string& base_url,
1180 const std::string& virtual_url,
1181 std::string* error) {
1182 // Make GURLs from URLs.
1183 const GURL data_gurl = GURL(data_url);
1184 const GURL base_gurl = GURL(base_url);
1185 const GURL virtual_gurl = GURL(virtual_url);
1187 // Check that the provided URLs are valid.
1188 // |data_url| must be a valid data URL.
1189 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1190 base::SStringPrintf(
1191 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1192 return false;
1194 // |base_url| must be a valid URL.
1195 if (!base_gurl.is_valid()) {
1196 base::SStringPrintf(
1197 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1198 return false;
1200 // |virtual_url| must be a valid URL.
1201 if (!virtual_gurl.is_valid()) {
1202 base::SStringPrintf(
1203 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1204 return false;
1207 // Set up the parameters to load |data_url| with the specified |base_url|.
1208 content::NavigationController::LoadURLParams load_params(data_gurl);
1209 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1210 load_params.base_url_for_data_url = base_gurl;
1211 load_params.virtual_url_for_data_url = virtual_gurl;
1212 load_params.override_user_agent =
1213 content::NavigationController::UA_OVERRIDE_INHERIT;
1215 // Navigate to the data URL.
1216 GuestViewBase::LoadURLWithParams(load_params);
1218 return true;
1221 void WebViewGuest::AddNewContents(WebContents* source,
1222 WebContents* new_contents,
1223 WindowOpenDisposition disposition,
1224 const gfx::Rect& initial_rect,
1225 bool user_gesture,
1226 bool* was_blocked) {
1227 if (was_blocked)
1228 *was_blocked = false;
1229 RequestNewWindowPermission(disposition,
1230 initial_rect,
1231 user_gesture,
1232 new_contents);
1235 WebContents* WebViewGuest::OpenURLFromTab(
1236 WebContents* source,
1237 const content::OpenURLParams& params) {
1238 // There are two use cases to consider from a security perspective:
1239 // 1.) Renderer-initiated navigation to chrome:// must always be blocked even
1240 // if the <webview> is in WebUI. This is handled by
1241 // WebViewGuest::LoadURLWithParams. WebViewGuest::NavigateGuest will also
1242 // call LoadURLWithParams. CreateNewGuestWebViewWindow creates a new
1243 // WebViewGuest which will call NavigateGuest in DidInitialize.
1244 // 2.) The Language Settings context menu item should always work, both in
1245 // Chrome Apps and WebUI. This is a browser initiated request and so
1246 // we pass it along to the embedder's WebContentsDelegate to get the
1247 // browser to perform the action for the <webview>.
1248 if (!params.is_renderer_initiated) {
1249 if (!owner_web_contents()->GetDelegate())
1250 return nullptr;
1251 return owner_web_contents()->GetDelegate()->OpenURLFromTab(
1252 owner_web_contents(), params);
1255 if (!attached()) {
1256 WebViewGuest* opener = GetOpener();
1257 // If the guest wishes to navigate away prior to attachment then we save the
1258 // navigation to perform upon attachment. Navigation initializes a lot of
1259 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1260 // Navigation also resumes resource loading. If we were created using
1261 // newwindow (i.e. we have an opener), we don't allow navigation until
1262 // attachment.
1263 if (opener) {
1264 auto it = opener->pending_new_windows_.find(this);
1265 if (it == opener->pending_new_windows_.end())
1266 return nullptr;
1267 const NewWindowInfo& info = it->second;
1268 NewWindowInfo new_window_info(params.url, info.name);
1269 new_window_info.changed = new_window_info.url != info.url;
1270 it->second = new_window_info;
1271 return nullptr;
1275 // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
1276 // decides that a fork should happen. At the time of writing this comment,
1277 // the only way a well behaving guest could hit this code path is if it
1278 // navigates to a URL that's associated with the default search engine.
1279 // This list of URLs is generated by search::GetSearchURLs. Validity checks
1280 // are performed inside LoadURLWithParams such that if the guest attempts
1281 // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
1282 // event will fire in the embedder, and the guest will be navigated to
1283 // about:blank.
1284 if (params.disposition == CURRENT_TAB) {
1285 LoadURLWithParams(params.url, params.referrer, params.transition,
1286 params.transferred_global_request_id,
1287 true /* force_navigation */);
1288 return web_contents();
1291 // This code path is taken if Ctrl+Click, middle click or any of the
1292 // keyboard/mouse combinations are used to open a link in a new tab/window.
1293 // This code path is also taken on client-side redirects from about:blank.
1294 CreateNewGuestWebViewWindow(params);
1295 return nullptr;
1298 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1299 int opener_render_frame_id,
1300 const std::string& frame_name,
1301 const GURL& target_url,
1302 WebContents* new_contents) {
1303 auto guest = WebViewGuest::FromWebContents(new_contents);
1304 CHECK(guest);
1305 guest->SetOpener(this);
1306 guest->name_ = frame_name;
1307 pending_new_windows_.insert(
1308 std::make_pair(guest, NewWindowInfo(target_url, frame_name)));
1311 void WebViewGuest::EnterFullscreenModeForTab(WebContents* web_contents,
1312 const GURL& origin) {
1313 // Ask the embedder for permission.
1314 base::DictionaryValue request_info;
1315 request_info.SetString(webview::kOrigin, origin.spec());
1316 web_view_permission_helper_->RequestPermission(
1317 WEB_VIEW_PERMISSION_TYPE_FULLSCREEN, request_info,
1318 base::Bind(&WebViewGuest::OnFullscreenPermissionDecided,
1319 weak_ptr_factory_.GetWeakPtr()),
1320 false /* allowed_by_default */);
1322 // TODO(lazyboy): Right now the guest immediately goes fullscreen within its
1323 // bounds. If the embedder denies the permission then we will see a flicker.
1324 // Once we have the ability to "cancel" a renderer/ fullscreen request:
1325 // http://crbug.com/466854 this won't be necessary and we should be
1326 // Calling SetFullscreenState(true) once the embedder allowed the request.
1327 // Otherwise we would cancel renderer/ fullscreen if the embedder denied.
1328 SetFullscreenState(true);
1331 void WebViewGuest::ExitFullscreenModeForTab(WebContents* web_contents) {
1332 SetFullscreenState(false);
1335 bool WebViewGuest::IsFullscreenForTabOrPending(
1336 const WebContents* web_contents) const {
1337 return is_guest_fullscreen_;
1340 void WebViewGuest::LoadURLWithParams(
1341 const GURL& url,
1342 const content::Referrer& referrer,
1343 ui::PageTransition transition_type,
1344 const GlobalRequestID& transferred_global_request_id,
1345 bool force_navigation) {
1346 if (!url.is_valid()) {
1347 LoadAbort(true /* is_top_level */, url, net::ERR_INVALID_URL,
1348 net::ErrorToShortString(net::ERR_INVALID_URL));
1349 NavigateGuest(url::kAboutBlankURL, false /* force_navigation */);
1350 return;
1353 bool scheme_is_blocked =
1354 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
1355 url.scheme()) &&
1356 !url.SchemeIs(url::kAboutScheme)) ||
1357 url.SchemeIs(url::kJavaScriptScheme);
1359 // Do not allow navigating a guest to schemes other than known safe schemes.
1360 // This will block the embedder trying to load unwanted schemes, e.g.
1361 // chrome://.
1362 if (scheme_is_blocked) {
1363 LoadAbort(true /* is_top_level */, url, net::ERR_DISALLOWED_URL_SCHEME,
1364 net::ErrorToShortString(net::ERR_DISALLOWED_URL_SCHEME));
1365 NavigateGuest(url::kAboutBlankURL, false /* force_navigation */);
1366 return;
1369 if (!force_navigation && (src_ == url))
1370 return;
1372 GURL validated_url(url);
1373 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
1374 // As guests do not swap processes on navigation, only navigations to
1375 // normal web URLs are supported. No protocol handlers are installed for
1376 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
1377 // can be granted to the guest process.
1378 content::NavigationController::LoadURLParams load_url_params(validated_url);
1379 load_url_params.referrer = referrer;
1380 load_url_params.transition_type = transition_type;
1381 load_url_params.extra_headers = std::string();
1382 load_url_params.transferred_global_request_id = transferred_global_request_id;
1383 if (is_overriding_user_agent_) {
1384 load_url_params.override_user_agent =
1385 content::NavigationController::UA_OVERRIDE_TRUE;
1387 GuestViewBase::LoadURLWithParams(load_url_params);
1389 src_ = validated_url;
1392 void WebViewGuest::RequestNewWindowPermission(WindowOpenDisposition disposition,
1393 const gfx::Rect& initial_bounds,
1394 bool user_gesture,
1395 WebContents* new_contents) {
1396 auto guest = WebViewGuest::FromWebContents(new_contents);
1397 if (!guest)
1398 return;
1399 auto it = pending_new_windows_.find(guest);
1400 if (it == pending_new_windows_.end())
1401 return;
1402 const NewWindowInfo& new_window_info = it->second;
1404 // Retrieve the opener partition info if we have it.
1405 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1406 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1408 base::DictionaryValue request_info;
1409 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1410 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1411 request_info.Set(webview::kTargetURL,
1412 new base::StringValue(new_window_info.url.spec()));
1413 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1414 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1415 // We pass in partition info so that window-s created through newwindow
1416 // API can use it to set their partition attribute.
1417 request_info.Set(webview::kStoragePartitionId,
1418 new base::StringValue(storage_partition_id));
1419 request_info.Set(
1420 webview::kWindowOpenDisposition,
1421 new base::StringValue(WindowOpenDispositionToString(disposition)));
1423 web_view_permission_helper_->
1424 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1425 request_info,
1426 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1427 weak_ptr_factory_.GetWeakPtr(),
1428 guest->guest_instance_id()),
1429 false /* allowed_by_default */);
1432 GURL WebViewGuest::ResolveURL(const std::string& src) {
1433 if (!GuestViewManager::FromBrowserContext(browser_context())->
1434 IsOwnedByExtension(this)) {
1435 return GURL(src);
1438 GURL default_url(base::StringPrintf("%s://%s/",
1439 kExtensionScheme,
1440 owner_host().c_str()));
1441 return default_url.Resolve(src);
1444 void WebViewGuest::OnWebViewNewWindowResponse(
1445 int new_window_instance_id,
1446 bool allow,
1447 const std::string& user_input) {
1448 auto guest =
1449 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1450 new_window_instance_id);
1451 if (!guest)
1452 return;
1454 if (!allow)
1455 guest->Destroy();
1458 void WebViewGuest::OnFullscreenPermissionDecided(
1459 bool allowed,
1460 const std::string& user_input) {
1461 last_fullscreen_permission_was_allowed_by_embedder_ = allowed;
1462 SetFullscreenState(allowed);
1465 bool WebViewGuest::GuestMadeEmbedderFullscreen() const {
1466 return last_fullscreen_permission_was_allowed_by_embedder_ &&
1467 is_embedder_fullscreen_;
1470 void WebViewGuest::SetFullscreenState(bool is_fullscreen) {
1471 if (is_fullscreen == is_guest_fullscreen_)
1472 return;
1474 bool was_fullscreen = is_guest_fullscreen_;
1475 is_guest_fullscreen_ = is_fullscreen;
1476 // If the embedder entered fullscreen because of us, it should exit fullscreen
1477 // when we exit fullscreen.
1478 if (was_fullscreen && GuestMadeEmbedderFullscreen()) {
1479 // Dispatch a message so we can call document.webkitCancelFullscreen()
1480 // on the embedder.
1481 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1482 DispatchEventToView(
1483 new GuestViewEvent(webview::kEventExitFullscreen, args.Pass()));
1485 // Since we changed fullscreen state, sending a Resize message ensures that
1486 // renderer/ sees the change.
1487 web_contents()->GetRenderViewHost()->WasResized();
1490 } // namespace extensions