[ServiceWorker] Implement WebServiceWorkerContextClient::openWindow().
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob36418980eadde3a8fa1d9fd78e76eafe7215b33b
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/public/browser/browser_context.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/child_process_security_policy.h"
13 #include "content/public/browser/native_web_keyboard_event.h"
14 #include "content/public/browser/navigation_entry.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/resource_request_details.h"
23 #include "content/public/browser/site_instance.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/browser/user_metrics.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/common/media_stream_request.h"
29 #include "content/public/common/page_zoom.h"
30 #include "content/public/common/result_codes.h"
31 #include "content/public/common/stop_find_action.h"
32 #include "content/public/common/url_constants.h"
33 #include "extensions/browser/api/declarative/rules_registry_service.h"
34 #include "extensions/browser/api/extensions_api_client.h"
35 #include "extensions/browser/api/web_request/web_request_api.h"
36 #include "extensions/browser/api/web_view/web_view_internal_api.h"
37 #include "extensions/browser/extension_system.h"
38 #include "extensions/browser/guest_view/guest_view_manager.h"
39 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
40 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
41 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
42 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
43 #include "extensions/common/constants.h"
44 #include "extensions/common/extension_messages.h"
45 #include "extensions/common/guest_view/guest_view_constants.h"
46 #include "extensions/strings/grit/extensions_strings.h"
47 #include "ipc/ipc_message_macros.h"
48 #include "net/base/escape.h"
49 #include "net/base/net_errors.h"
50 #include "ui/base/models/simple_menu_model.h"
51 #include "url/url_constants.h"
53 using base::UserMetricsAction;
54 using content::RenderFrameHost;
55 using content::ResourceType;
56 using content::WebContents;
58 namespace extensions {
60 namespace {
62 std::string WindowOpenDispositionToString(
63 WindowOpenDisposition window_open_disposition) {
64 switch (window_open_disposition) {
65 case IGNORE_ACTION:
66 return "ignore";
67 case SAVE_TO_DISK:
68 return "save_to_disk";
69 case CURRENT_TAB:
70 return "current_tab";
71 case NEW_BACKGROUND_TAB:
72 return "new_background_tab";
73 case NEW_FOREGROUND_TAB:
74 return "new_foreground_tab";
75 case NEW_WINDOW:
76 return "new_window";
77 case NEW_POPUP:
78 return "new_popup";
79 default:
80 NOTREACHED() << "Unknown Window Open Disposition";
81 return "ignore";
85 static std::string TerminationStatusToString(base::TerminationStatus status) {
86 switch (status) {
87 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
88 return "normal";
89 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
90 case base::TERMINATION_STATUS_STILL_RUNNING:
91 return "abnormal";
92 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
93 return "killed";
94 case base::TERMINATION_STATUS_PROCESS_CRASHED:
95 return "crashed";
96 case base::TERMINATION_STATUS_MAX_ENUM:
97 break;
99 NOTREACHED() << "Unknown Termination Status.";
100 return "unknown";
103 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
104 const std::string& partition_id = site_url.query();
105 bool persist_storage = site_url.path().find("persist") != std::string::npos;
106 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
109 void ParsePartitionParam(const base::DictionaryValue& create_params,
110 std::string* storage_partition_id,
111 bool* persist_storage) {
112 std::string partition_str;
113 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
114 return;
117 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
118 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
119 // remove the prefix without splicing in the middle of a multi-byte codepoint.
120 // We can use the rest of the string as UTF-8 encoded one.
121 if (StartsWithASCII(partition_str, "persist:", true)) {
122 size_t index = partition_str.find(":");
123 CHECK(index != std::string::npos);
124 // It is safe to do index + 1, since we tested for the full prefix above.
125 *storage_partition_id = partition_str.substr(index + 1);
127 if (storage_partition_id->empty()) {
128 // TODO(lazyboy): Better way to deal with this error.
129 return;
131 *persist_storage = true;
132 } else {
133 *storage_partition_id = partition_str;
134 *persist_storage = false;
138 void RemoveWebViewEventListenersOnIOThread(
139 void* profile,
140 const std::string& extension_id,
141 int embedder_process_id,
142 int view_instance_id) {
143 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
144 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
145 profile,
146 extension_id,
147 embedder_process_id,
148 view_instance_id);
151 } // namespace
153 // static
154 GuestViewBase* WebViewGuest::Create(content::WebContents* owner_web_contents) {
155 return new WebViewGuest(owner_web_contents);
158 // static
159 bool WebViewGuest::GetGuestPartitionConfigForSite(
160 const GURL& site,
161 std::string* partition_domain,
162 std::string* partition_name,
163 bool* in_memory) {
164 if (!site.SchemeIs(content::kGuestScheme))
165 return false;
167 // Since guest URLs are only used for packaged apps, there must be an app
168 // id in the URL.
169 CHECK(site.has_host());
170 *partition_domain = site.host();
171 // Since persistence is optional, the path must either be empty or the
172 // literal string.
173 *in_memory = (site.path() != "/persist");
174 // The partition name is user supplied value, which we have encoded when the
175 // URL was created, so it needs to be decoded.
176 *partition_name =
177 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
178 return true;
181 // static
182 const char WebViewGuest::Type[] = "webview";
184 using WebViewKey = std::pair<int, int>;
185 using WebViewKeyToIDMap = std::map<WebViewKey, int>;
186 static base::LazyInstance<WebViewKeyToIDMap> web_view_key_to_id_map =
187 LAZY_INSTANCE_INITIALIZER;
189 // static
190 int WebViewGuest::GetOrGenerateRulesRegistryID(
191 int embedder_process_id,
192 int webview_instance_id) {
193 bool is_web_view = embedder_process_id && webview_instance_id;
194 if (!is_web_view)
195 return RulesRegistryService::kDefaultRulesRegistryID;
197 WebViewKey key = std::make_pair(embedder_process_id, webview_instance_id);
198 auto it = web_view_key_to_id_map.Get().find(key);
199 if (it != web_view_key_to_id_map.Get().end())
200 return it->second;
202 auto rph = content::RenderProcessHost::FromID(embedder_process_id);
203 int rules_registry_id =
204 RulesRegistryService::Get(rph->GetBrowserContext())->
205 GetNextRulesRegistryID();
206 web_view_key_to_id_map.Get()[key] = rules_registry_id;
207 return rules_registry_id;
210 // static
211 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
212 auto guest = FromWebContents(contents);
213 if (!guest)
214 return guestview::kInstanceIDNone;
216 return guest->view_instance_id();
219 bool WebViewGuest::CanRunInDetachedState() const {
220 return true;
223 void WebViewGuest::CreateWebContents(
224 const base::DictionaryValue& create_params,
225 const WebContentsCreatedCallback& callback) {
226 content::RenderProcessHost* owner_render_process_host =
227 owner_web_contents()->GetRenderProcessHost();
228 std::string storage_partition_id;
229 bool persist_storage = false;
230 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
231 // Validate that the partition id coming from the renderer is valid UTF-8,
232 // since we depend on this in other parts of the code, such as FilePath
233 // creation. If the validation fails, treat it as a bad message and kill the
234 // renderer process.
235 if (!base::IsStringUTF8(storage_partition_id)) {
236 content::RecordAction(
237 base::UserMetricsAction("BadMessageTerminate_BPGM"));
238 owner_render_process_host->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE,
239 false);
240 callback.Run(nullptr);
241 return;
243 std::string url_encoded_partition = net::EscapeQueryParamValue(
244 storage_partition_id, false);
245 std::string partition_domain = GetOwnerSiteURL().host();
246 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
247 content::kGuestScheme,
248 partition_domain.c_str(),
249 persist_storage ? "persist" : "",
250 url_encoded_partition.c_str()));
252 // If we already have a webview tag in the same app using the same storage
253 // partition, we should use the same SiteInstance so the existing tag and
254 // the new tag can script each other.
255 auto guest_view_manager = GuestViewManager::FromBrowserContext(
256 owner_render_process_host->GetBrowserContext());
257 content::SiteInstance* guest_site_instance =
258 guest_view_manager->GetGuestSiteInstance(guest_site);
259 if (!guest_site_instance) {
260 // Create the SiteInstance in a new BrowsingInstance, which will ensure
261 // that webview tags are also not allowed to send messages across
262 // different partitions.
263 guest_site_instance = content::SiteInstance::CreateForURL(
264 owner_render_process_host->GetBrowserContext(), guest_site);
266 WebContents::CreateParams params(
267 owner_render_process_host->GetBrowserContext(),
268 guest_site_instance);
269 params.guest_delegate = this;
270 callback.Run(WebContents::Create(params));
273 void WebViewGuest::DidAttachToEmbedder() {
274 ApplyAttributes(*attach_params());
277 void WebViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
278 script_executor_.reset(
279 new ScriptExecutor(web_contents(), &script_observers_));
281 notification_registrar_.Add(this,
282 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
283 content::Source<WebContents>(web_contents()));
285 notification_registrar_.Add(this,
286 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
287 content::Source<WebContents>(web_contents()));
289 if (web_view_guest_delegate_)
290 web_view_guest_delegate_->OnDidInitialize();
291 AttachWebViewHelpers(web_contents());
293 rules_registry_id_ = GetOrGenerateRulesRegistryID(
294 owner_web_contents()->GetRenderProcessHost()->GetID(),
295 view_instance_id());
297 // We must install the mapping from guests to WebViews prior to resuming
298 // suspended resource loads so that the WebRequest API will catch resource
299 // requests.
300 PushWebViewStateToIOThread();
302 ApplyAttributes(create_params);
305 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
306 if (web_view_guest_delegate_)
307 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
308 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
311 void WebViewGuest::DidStopLoading() {
312 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
313 DispatchEventToView(
314 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
317 void WebViewGuest::EmbedderWillBeDestroyed() {
318 // Clean up rules registries for the webview.
319 RulesRegistryService::Get(browser_context())
320 ->RemoveRulesRegistriesByID(rules_registry_id_);
321 WebViewKey key(owner_web_contents()->GetRenderProcessHost()->GetID(),
322 view_instance_id());
323 web_view_key_to_id_map.Get().erase(key);
325 content::BrowserThread::PostTask(
326 content::BrowserThread::IO,
327 FROM_HERE,
328 base::Bind(
329 &RemoveWebViewEventListenersOnIOThread,
330 browser_context(),
331 owner_extension_id(),
332 owner_web_contents()->GetRenderProcessHost()->GetID(),
333 view_instance_id()));
336 const char* WebViewGuest::GetAPINamespace() const {
337 return webview::kAPINamespace;
340 int WebViewGuest::GetTaskPrefix() const {
341 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
344 void WebViewGuest::GuestDestroyed() {
345 // Clean up custom context menu items for this guest.
346 if (web_view_guest_delegate_)
347 web_view_guest_delegate_->OnGuestDestroyed();
348 RemoveWebViewStateFromIOThread(web_contents());
351 void WebViewGuest::GuestReady() {
352 // The guest RenderView should always live in an isolated guest process.
353 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
354 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
356 // We don't want to accidentally set the opacity of an interstitial page.
357 // WebContents::GetRenderWidgetHostView will return the RWHV of an
358 // interstitial page if one is showing at this time. We only want opacity
359 // to apply to web pages.
360 if (guest_opaque_) {
361 web_contents()
362 ->GetRenderViewHost()
363 ->GetView()
364 ->SetBackgroundColorToDefault();
365 } else {
366 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
367 SK_ColorTRANSPARENT);
371 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
372 const gfx::Size& new_size) {
373 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
374 args->SetInteger(webview::kOldHeight, old_size.height());
375 args->SetInteger(webview::kOldWidth, old_size.width());
376 args->SetInteger(webview::kNewHeight, new_size.height());
377 args->SetInteger(webview::kNewWidth, new_size.width());
378 DispatchEventToView(
379 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
382 bool WebViewGuest::IsAutoSizeSupported() const {
383 return true;
386 bool WebViewGuest::IsDragAndDropEnabled() const {
387 return true;
390 void WebViewGuest::WillDestroy() {
391 if (!attached() && GetOpener())
392 GetOpener()->pending_new_windows_.erase(this);
395 bool WebViewGuest::AddMessageToConsole(WebContents* source,
396 int32 level,
397 const base::string16& message,
398 int32 line_no,
399 const base::string16& source_id) {
400 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
401 // Log levels are from base/logging.h: LogSeverity.
402 args->SetInteger(webview::kLevel, level);
403 args->SetString(webview::kMessage, message);
404 args->SetInteger(webview::kLine, line_no);
405 args->SetString(webview::kSourceId, source_id);
406 DispatchEventToView(
407 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
408 return true;
411 void WebViewGuest::CloseContents(WebContents* source) {
412 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
413 DispatchEventToView(
414 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
417 void WebViewGuest::FindReply(WebContents* source,
418 int request_id,
419 int number_of_matches,
420 const gfx::Rect& selection_rect,
421 int active_match_ordinal,
422 bool final_update) {
423 find_helper_.FindReply(request_id,
424 number_of_matches,
425 selection_rect,
426 active_match_ordinal,
427 final_update);
430 bool WebViewGuest::HandleContextMenu(
431 const content::ContextMenuParams& params) {
432 if (!web_view_guest_delegate_)
433 return false;
434 return web_view_guest_delegate_->HandleContextMenu(params);
437 void WebViewGuest::HandleKeyboardEvent(
438 WebContents* source,
439 const content::NativeWebKeyboardEvent& event) {
440 if (HandleKeyboardShortcuts(event))
441 return;
443 GuestViewBase::HandleKeyboardEvent(source, event);
446 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
447 double progress) {
448 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
449 args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
450 args->SetDouble(webview::kProgress, progress);
451 DispatchEventToView(
452 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
455 void WebViewGuest::LoadAbort(bool is_top_level,
456 const GURL& url,
457 const std::string& error_type) {
458 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
459 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
460 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
461 args->SetString(guestview::kReason, error_type);
462 DispatchEventToView(
463 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
466 void WebViewGuest::OnFrameNameChanged(bool is_top_level,
467 const std::string& name) {
468 if (!is_top_level)
469 return;
471 if (name_ == name)
472 return;
474 ReportFrameNameChange(name);
477 void WebViewGuest::CreateNewGuestWebViewWindow(
478 const content::OpenURLParams& params) {
479 GuestViewManager* guest_manager =
480 GuestViewManager::FromBrowserContext(browser_context());
481 // Set the attach params to use the same partition as the opener.
482 // We pull the partition information from the site's URL, which is of the
483 // form guest://site/{persist}?{partition_name}.
484 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
485 const std::string storage_partition_id =
486 GetStoragePartitionIdFromSiteURL(site_url);
487 base::DictionaryValue create_params;
488 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
490 guest_manager->CreateGuest(WebViewGuest::Type,
491 embedder_web_contents(),
492 create_params,
493 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
494 weak_ptr_factory_.GetWeakPtr(),
495 params));
498 void WebViewGuest::NewGuestWebViewCallback(
499 const content::OpenURLParams& params,
500 content::WebContents* guest_web_contents) {
501 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
502 new_guest->SetOpener(this);
504 // Take ownership of |new_guest|.
505 pending_new_windows_.insert(
506 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
508 // Request permission to show the new window.
509 RequestNewWindowPermission(params.disposition,
510 gfx::Rect(),
511 params.user_gesture,
512 new_guest->web_contents());
515 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
516 // 'unresponsive' events.
517 void WebViewGuest::RendererResponsive(content::WebContents* source) {
518 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
519 args->SetInteger(webview::kProcessId,
520 web_contents()->GetRenderProcessHost()->GetID());
521 DispatchEventToView(
522 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
525 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
526 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
527 args->SetInteger(webview::kProcessId,
528 web_contents()->GetRenderProcessHost()->GetID());
529 DispatchEventToView(
530 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
533 void WebViewGuest::Observe(int type,
534 const content::NotificationSource& source,
535 const content::NotificationDetails& details) {
536 switch (type) {
537 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
538 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
539 if (content::Source<WebContents>(source).ptr() == web_contents())
540 LoadHandlerCalled();
541 break;
543 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
544 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
545 content::ResourceRedirectDetails* resource_redirect_details =
546 content::Details<content::ResourceRedirectDetails>(details).ptr();
547 bool is_top_level = resource_redirect_details->resource_type ==
548 content::RESOURCE_TYPE_MAIN_FRAME;
549 LoadRedirect(resource_redirect_details->url,
550 resource_redirect_details->new_url,
551 is_top_level);
552 break;
554 default:
555 NOTREACHED() << "Unexpected notification sent.";
556 break;
560 void WebViewGuest::StartFinding(
561 const base::string16& search_text,
562 const blink::WebFindOptions& options,
563 scoped_refptr<WebViewInternalFindFunction> find_function) {
564 find_helper_.Find(web_contents(), search_text, options, find_function);
567 void WebViewGuest::StopFinding(content::StopFindAction action) {
568 find_helper_.CancelAllFindSessions();
569 web_contents()->StopFinding(action);
572 bool WebViewGuest::Go(int relative_index) {
573 content::NavigationController& controller = web_contents()->GetController();
574 if (!controller.CanGoToOffset(relative_index))
575 return false;
577 controller.GoToOffset(relative_index);
578 return true;
581 void WebViewGuest::Reload() {
582 // TODO(fsamuel): Don't check for repost because we don't want to show
583 // Chromium's repost warning. We might want to implement a separate API
584 // for registering a callback if a repost is about to happen.
585 web_contents()->GetController().Reload(false);
588 void WebViewGuest::SetUserAgentOverride(
589 const std::string& user_agent_override) {
590 is_overriding_user_agent_ = !user_agent_override.empty();
591 if (is_overriding_user_agent_) {
592 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
594 web_contents()->SetUserAgentOverride(user_agent_override);
597 void WebViewGuest::Stop() {
598 web_contents()->Stop();
601 void WebViewGuest::Terminate() {
602 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
603 base::ProcessHandle process_handle =
604 web_contents()->GetRenderProcessHost()->GetHandle();
605 if (process_handle)
606 web_contents()->GetRenderProcessHost()->Shutdown(
607 content::RESULT_CODE_KILLED, false);
610 bool WebViewGuest::ClearData(const base::Time remove_since,
611 uint32 removal_mask,
612 const base::Closure& callback) {
613 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
614 content::StoragePartition* partition =
615 content::BrowserContext::GetStoragePartition(
616 web_contents()->GetBrowserContext(),
617 web_contents()->GetSiteInstance());
619 if (!partition)
620 return false;
622 partition->ClearData(
623 removal_mask,
624 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
625 GURL(),
626 content::StoragePartition::OriginMatcherFunction(),
627 remove_since,
628 base::Time::Now(),
629 callback);
630 return true;
633 WebViewGuest::WebViewGuest(content::WebContents* owner_web_contents)
634 : GuestView<WebViewGuest>(owner_web_contents),
635 rules_registry_id_(RulesRegistryService::kInvalidRulesRegistryID),
636 find_helper_(this),
637 is_overriding_user_agent_(false),
638 guest_opaque_(true),
639 javascript_dialog_helper_(this),
640 current_zoom_factor_(1.0),
641 weak_ptr_factory_(this) {
642 web_view_guest_delegate_.reset(
643 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
646 WebViewGuest::~WebViewGuest() {
649 void WebViewGuest::DidCommitProvisionalLoadForFrame(
650 content::RenderFrameHost* render_frame_host,
651 const GURL& url,
652 ui::PageTransition transition_type) {
653 if (!render_frame_host->GetParent())
654 src_ = url;
655 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
656 args->SetString(guestview::kUrl, url.spec());
657 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
658 args->SetString(webview::kInternalBaseURLForDataURL,
659 web_contents()
660 ->GetController()
661 .GetLastCommittedEntry()
662 ->GetBaseURLForDataURL()
663 .spec());
664 args->SetInteger(webview::kInternalCurrentEntryIndex,
665 web_contents()->GetController().GetCurrentEntryIndex());
666 args->SetInteger(webview::kInternalEntryCount,
667 web_contents()->GetController().GetEntryCount());
668 args->SetInteger(webview::kInternalProcessId,
669 web_contents()->GetRenderProcessHost()->GetID());
670 DispatchEventToView(
671 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
673 find_helper_.CancelAllFindSessions();
675 // Update the current zoom factor for the new page.
676 ui_zoom::ZoomController* zoom_controller =
677 ui_zoom::ZoomController::FromWebContents(web_contents());
678 DCHECK(zoom_controller);
679 current_zoom_factor_ =
680 content::ZoomLevelToZoomFactor(zoom_controller->GetZoomLevel());
682 if (web_view_guest_delegate_) {
683 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
684 !render_frame_host->GetParent());
688 void WebViewGuest::DidFailProvisionalLoad(
689 content::RenderFrameHost* render_frame_host,
690 const GURL& validated_url,
691 int error_code,
692 const base::string16& error_description) {
693 LoadAbort(!render_frame_host->GetParent(), validated_url,
694 net::ErrorToShortString(error_code));
697 void WebViewGuest::DidStartProvisionalLoadForFrame(
698 content::RenderFrameHost* render_frame_host,
699 const GURL& validated_url,
700 bool is_error_page,
701 bool is_iframe_srcdoc) {
702 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
703 args->SetString(guestview::kUrl, validated_url.spec());
704 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
705 DispatchEventToView(
706 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
709 void WebViewGuest::DocumentLoadedInFrame(
710 content::RenderFrameHost* render_frame_host) {
711 if (web_view_guest_delegate_)
712 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
715 bool WebViewGuest::OnMessageReceived(const IPC::Message& message,
716 RenderFrameHost* render_frame_host) {
717 bool handled = true;
718 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message)
719 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged)
720 IPC_MESSAGE_UNHANDLED(handled = false)
721 IPC_END_MESSAGE_MAP()
722 return handled;
725 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
726 // Cancel all find sessions in progress.
727 find_helper_.CancelAllFindSessions();
729 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
730 args->SetInteger(webview::kProcessId,
731 web_contents()->GetRenderProcessHost()->GetID());
732 args->SetString(webview::kReason, TerminationStatusToString(status));
733 DispatchEventToView(
734 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
737 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
738 content::NavigationController& controller = web_contents()->GetController();
739 content::NavigationEntry* entry = controller.GetVisibleEntry();
740 if (!entry)
741 return;
742 entry->SetIsOverridingUserAgent(!user_agent.empty());
743 web_contents()->GetController().Reload(false);
746 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
747 name_ = name;
748 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
749 args->SetString(webview::kName, name);
750 DispatchEventToView(
751 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
754 void WebViewGuest::LoadHandlerCalled() {
755 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
756 DispatchEventToView(
757 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass()));
760 void WebViewGuest::LoadRedirect(const GURL& old_url,
761 const GURL& new_url,
762 bool is_top_level) {
763 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
764 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
765 args->SetString(webview::kNewURL, new_url.spec());
766 args->SetString(webview::kOldURL, old_url.spec());
767 DispatchEventToView(
768 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass()));
771 void WebViewGuest::PushWebViewStateToIOThread() {
772 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
773 std::string partition_domain;
774 std::string partition_id;
775 bool in_memory;
776 if (!GetGuestPartitionConfigForSite(
777 site_url, &partition_domain, &partition_id, &in_memory)) {
778 NOTREACHED();
779 return;
782 WebViewRendererState::WebViewInfo web_view_info;
783 web_view_info.embedder_process_id =
784 owner_web_contents()->GetRenderProcessHost()->GetID();
785 web_view_info.instance_id = view_instance_id();
786 web_view_info.partition_id = partition_id;
787 web_view_info.owner_extension_id = owner_extension_id();
788 web_view_info.rules_registry_id = rules_registry_id_;
790 content::BrowserThread::PostTask(
791 content::BrowserThread::IO,
792 FROM_HERE,
793 base::Bind(&WebViewRendererState::AddGuest,
794 base::Unretained(WebViewRendererState::GetInstance()),
795 web_contents()->GetRenderProcessHost()->GetID(),
796 web_contents()->GetRoutingID(),
797 web_view_info));
800 // static
801 void WebViewGuest::RemoveWebViewStateFromIOThread(
802 WebContents* web_contents) {
803 content::BrowserThread::PostTask(
804 content::BrowserThread::IO, FROM_HERE,
805 base::Bind(
806 &WebViewRendererState::RemoveGuest,
807 base::Unretained(WebViewRendererState::GetInstance()),
808 web_contents->GetRenderProcessHost()->GetID(),
809 web_contents->GetRoutingID()));
812 void WebViewGuest::RequestMediaAccessPermission(
813 content::WebContents* source,
814 const content::MediaStreamRequest& request,
815 const content::MediaResponseCallback& callback) {
816 web_view_permission_helper_->RequestMediaAccessPermission(source,
817 request,
818 callback);
821 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
822 const GURL& security_origin,
823 content::MediaStreamType type) {
824 return web_view_permission_helper_->CheckMediaAccessPermission(
825 source, security_origin, type);
828 void WebViewGuest::CanDownload(
829 content::RenderViewHost* render_view_host,
830 const GURL& url,
831 const std::string& request_method,
832 const base::Callback<void(bool)>& callback) {
833 web_view_permission_helper_->CanDownload(render_view_host,
834 url,
835 request_method,
836 callback);
839 void WebViewGuest::RequestPointerLockPermission(
840 bool user_gesture,
841 bool last_unlocked_by_target,
842 const base::Callback<void(bool)>& callback) {
843 web_view_permission_helper_->RequestPointerLockPermission(
844 user_gesture,
845 last_unlocked_by_target,
846 callback);
849 void WebViewGuest::WillAttachToEmbedder() {
850 rules_registry_id_ = GetOrGenerateRulesRegistryID(
851 owner_web_contents()->GetRenderProcessHost()->GetID(),
852 view_instance_id());
854 // We must install the mapping from guests to WebViews prior to resuming
855 // suspended resource loads so that the WebRequest API will catch resource
856 // requests.
857 PushWebViewStateToIOThread();
860 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
861 WebContents* source) {
862 return &javascript_dialog_helper_;
865 void WebViewGuest::NavigateGuest(const std::string& src,
866 bool force_navigation) {
867 if (src.empty())
868 return;
870 GURL url = ResolveURL(src);
872 // Do not allow navigating a guest to schemes other than known safe schemes.
873 // This will block the embedder trying to load unwanted schemes, e.g.
874 // chrome://settings.
875 bool scheme_is_blocked =
876 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
877 url.scheme()) &&
878 !url.SchemeIs(url::kAboutScheme)) ||
879 url.SchemeIs(url::kJavaScriptScheme);
880 if (scheme_is_blocked || !url.is_valid()) {
881 LoadAbort(true /* is_top_level */, url,
882 net::ErrorToShortString(net::ERR_ABORTED));
883 NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
884 return;
886 if (!force_navigation && (src_ == url))
887 return;
889 GURL validated_url(url);
890 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
891 // As guests do not swap processes on navigation, only navigations to
892 // normal web URLs are supported. No protocol handlers are installed for
893 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
894 // can be granted to the guest process.
895 LoadURLWithParams(validated_url,
896 content::Referrer(),
897 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
898 web_contents());
900 src_ = validated_url;
903 bool WebViewGuest::HandleKeyboardShortcuts(
904 const content::NativeWebKeyboardEvent& event) {
905 if (event.type != blink::WebInputEvent::RawKeyDown)
906 return false;
908 // If the user hits the escape key without any modifiers then unlock the
909 // mouse if necessary.
910 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
911 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
912 return web_contents()->GotResponseToLockMouseRequest(false);
915 #if defined(OS_MACOSX)
916 if (event.modifiers != blink::WebInputEvent::MetaKey)
917 return false;
919 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
920 Go(-1);
921 return true;
924 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
925 Go(1);
926 return true;
928 #else
929 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
930 Go(-1);
931 return true;
934 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
935 Go(1);
936 return true;
938 #endif
940 return false;
943 void WebViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
944 std::string name;
945 if (params.GetString(webview::kAttributeName, &name)) {
946 // If the guest window's name is empty, then the WebView tag's name is
947 // assigned. Otherwise, the guest window's name takes precedence over the
948 // WebView tag's name.
949 if (name_.empty())
950 SetName(name);
952 if (attached())
953 ReportFrameNameChange(name_);
955 std::string user_agent_override;
956 params.GetString(webview::kParameterUserAgentOverride, &user_agent_override);
957 SetUserAgentOverride(user_agent_override);
959 bool allow_transparency = false;
960 params.GetBoolean(webview::kAttributeAllowTransparency, &allow_transparency);
961 // We need to set the background opaque flag after navigation to ensure that
962 // there is a RenderWidgetHostView available.
963 SetAllowTransparency(allow_transparency);
965 bool is_pending_new_window = false;
966 if (GetOpener()) {
967 // We need to do a navigation here if the target URL has changed between
968 // the time the WebContents was created and the time it was attached.
969 // We also need to do an initial navigation if a RenderView was never
970 // created for the new window in cases where there is no referrer.
971 auto it = GetOpener()->pending_new_windows_.find(this);
972 if (it != GetOpener()->pending_new_windows_.end()) {
973 const NewWindowInfo& new_window_info = it->second;
974 if (new_window_info.changed || !web_contents()->HasOpener())
975 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
977 // Once a new guest is attached to the DOM of the embedder page, then the
978 // lifetime of the new guest is no longer managed by the opener guest.
979 GetOpener()->pending_new_windows_.erase(this);
981 is_pending_new_window = true;
985 // Only read the src attribute if this is not a New Window API flow.
986 if (!is_pending_new_window) {
987 std::string src;
988 params.GetString(webview::kAttributeSrc, &src);
989 NavigateGuest(src, false /* force_navigation */);
994 void WebViewGuest::ShowContextMenu(
995 int request_id,
996 const WebViewGuestDelegate::MenuItemVector* items) {
997 if (web_view_guest_delegate_)
998 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1001 void WebViewGuest::SetName(const std::string& name) {
1002 if (name_ == name)
1003 return;
1004 name_ = name;
1006 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1009 void WebViewGuest::SetZoom(double zoom_factor) {
1010 auto zoom_controller =
1011 ui_zoom::ZoomController::FromWebContents(web_contents());
1012 DCHECK(zoom_controller);
1013 double zoom_level = content::ZoomFactorToZoomLevel(zoom_factor);
1014 zoom_controller->SetZoomLevel(zoom_level);
1016 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
1017 args->SetDouble(webview::kOldZoomFactor, current_zoom_factor_);
1018 args->SetDouble(webview::kNewZoomFactor, zoom_factor);
1019 DispatchEventToView(
1020 new GuestViewBase::Event(webview::kEventZoomChange, args.Pass()));
1021 current_zoom_factor_ = zoom_factor;
1024 void WebViewGuest::SetAllowTransparency(bool allow) {
1025 if (guest_opaque_ != allow)
1026 return;
1028 guest_opaque_ = !allow;
1029 if (!web_contents()->GetRenderViewHost()->GetView())
1030 return;
1032 if (guest_opaque_) {
1033 web_contents()
1034 ->GetRenderViewHost()
1035 ->GetView()
1036 ->SetBackgroundColorToDefault();
1037 } else {
1038 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1039 SK_ColorTRANSPARENT);
1043 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1044 const std::string& base_url,
1045 const std::string& virtual_url,
1046 std::string* error) {
1047 // Make GURLs from URLs.
1048 const GURL data_gurl = GURL(data_url);
1049 const GURL base_gurl = GURL(base_url);
1050 const GURL virtual_gurl = GURL(virtual_url);
1052 // Check that the provided URLs are valid.
1053 // |data_url| must be a valid data URL.
1054 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1055 base::SStringPrintf(
1056 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1057 return false;
1059 // |base_url| must be a valid URL.
1060 if (!base_gurl.is_valid()) {
1061 base::SStringPrintf(
1062 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1063 return false;
1065 // |virtual_url| must be a valid URL.
1066 if (!virtual_gurl.is_valid()) {
1067 base::SStringPrintf(
1068 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1069 return false;
1072 // Set up the parameters to load |data_url| with the specified |base_url|.
1073 content::NavigationController::LoadURLParams load_params(data_gurl);
1074 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1075 load_params.base_url_for_data_url = base_gurl;
1076 load_params.virtual_url_for_data_url = virtual_gurl;
1077 load_params.override_user_agent =
1078 content::NavigationController::UA_OVERRIDE_INHERIT;
1080 // Navigate to the data URL.
1081 web_contents()->GetController().LoadURLWithParams(load_params);
1083 return true;
1086 void WebViewGuest::AddNewContents(content::WebContents* source,
1087 content::WebContents* new_contents,
1088 WindowOpenDisposition disposition,
1089 const gfx::Rect& initial_rect,
1090 bool user_gesture,
1091 bool* was_blocked) {
1092 if (was_blocked)
1093 *was_blocked = false;
1094 RequestNewWindowPermission(disposition,
1095 initial_rect,
1096 user_gesture,
1097 new_contents);
1100 content::WebContents* WebViewGuest::OpenURLFromTab(
1101 content::WebContents* source,
1102 const content::OpenURLParams& params) {
1103 // If the guest wishes to navigate away prior to attachment then we save the
1104 // navigation to perform upon attachment. Navigation initializes a lot of
1105 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1106 // Navigation also resumes resource loading which we don't want to allow
1107 // until attachment.
1108 if (!attached()) {
1109 WebViewGuest* opener = GetOpener();
1110 auto it = opener->pending_new_windows_.find(this);
1111 if (it == opener->pending_new_windows_.end())
1112 return nullptr;
1113 const NewWindowInfo& info = it->second;
1114 NewWindowInfo new_window_info(params.url, info.name);
1115 new_window_info.changed = new_window_info.url != info.url;
1116 it->second = new_window_info;
1117 return nullptr;
1119 if (params.disposition == CURRENT_TAB) {
1120 // This can happen for cross-site redirects.
1121 LoadURLWithParams(params.url, params.referrer, params.transition, source);
1122 return source;
1125 CreateNewGuestWebViewWindow(params);
1126 return nullptr;
1129 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1130 int opener_render_frame_id,
1131 const base::string16& frame_name,
1132 const GURL& target_url,
1133 content::WebContents* new_contents) {
1134 auto guest = WebViewGuest::FromWebContents(new_contents);
1135 CHECK(guest);
1136 guest->SetOpener(this);
1137 std::string guest_name = base::UTF16ToUTF8(frame_name);
1138 guest->name_ = guest_name;
1139 pending_new_windows_.insert(
1140 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1143 void WebViewGuest::LoadURLWithParams(const GURL& url,
1144 const content::Referrer& referrer,
1145 ui::PageTransition transition_type,
1146 content::WebContents* web_contents) {
1147 content::NavigationController::LoadURLParams load_url_params(url);
1148 load_url_params.referrer = referrer;
1149 load_url_params.transition_type = transition_type;
1150 load_url_params.extra_headers = std::string();
1151 if (is_overriding_user_agent_) {
1152 load_url_params.override_user_agent =
1153 content::NavigationController::UA_OVERRIDE_TRUE;
1155 web_contents->GetController().LoadURLWithParams(load_url_params);
1158 void WebViewGuest::RequestNewWindowPermission(
1159 WindowOpenDisposition disposition,
1160 const gfx::Rect& initial_bounds,
1161 bool user_gesture,
1162 content::WebContents* new_contents) {
1163 auto guest = WebViewGuest::FromWebContents(new_contents);
1164 if (!guest)
1165 return;
1166 auto it = pending_new_windows_.find(guest);
1167 if (it == pending_new_windows_.end())
1168 return;
1169 const NewWindowInfo& new_window_info = it->second;
1171 // Retrieve the opener partition info if we have it.
1172 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1173 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1175 base::DictionaryValue request_info;
1176 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1177 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1178 request_info.Set(webview::kTargetURL,
1179 new base::StringValue(new_window_info.url.spec()));
1180 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1181 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1182 // We pass in partition info so that window-s created through newwindow
1183 // API can use it to set their partition attribute.
1184 request_info.Set(webview::kStoragePartitionId,
1185 new base::StringValue(storage_partition_id));
1186 request_info.Set(
1187 webview::kWindowOpenDisposition,
1188 new base::StringValue(WindowOpenDispositionToString(disposition)));
1190 web_view_permission_helper_->
1191 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1192 request_info,
1193 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1194 weak_ptr_factory_.GetWeakPtr(),
1195 guest->guest_instance_id()),
1196 false /* allowed_by_default */);
1199 GURL WebViewGuest::ResolveURL(const std::string& src) {
1200 if (!in_extension()) {
1201 return GURL(src);
1204 GURL default_url(base::StringPrintf("%s://%s/",
1205 kExtensionScheme,
1206 owner_extension_id().c_str()));
1207 return default_url.Resolve(src);
1210 void WebViewGuest::OnWebViewNewWindowResponse(
1211 int new_window_instance_id,
1212 bool allow,
1213 const std::string& user_input) {
1214 auto guest =
1215 WebViewGuest::From(owner_web_contents()->GetRenderProcessHost()->GetID(),
1216 new_window_instance_id);
1217 if (!guest)
1218 return;
1220 if (!allow)
1221 guest->Destroy();
1224 } // namespace extensions