Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob8433d52d8ba5f644e67e22b1abd050721c55c43f
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/extensions_api_client.h"
34 #include "extensions/browser/api/web_request/web_request_api.h"
35 #include "extensions/browser/api/web_view/web_view_internal_api.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/browser/guest_view/guest_view_manager.h"
38 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
39 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
40 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
41 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
42 #include "extensions/common/constants.h"
43 #include "extensions/common/extension_messages.h"
44 #include "extensions/common/guest_view/guest_view_constants.h"
45 #include "extensions/strings/grit/extensions_strings.h"
46 #include "ipc/ipc_message_macros.h"
47 #include "net/base/escape.h"
48 #include "net/base/net_errors.h"
49 #include "ui/base/models/simple_menu_model.h"
51 using base::UserMetricsAction;
52 using content::RenderFrameHost;
53 using content::ResourceType;
54 using content::WebContents;
56 namespace extensions {
58 namespace {
60 std::string WindowOpenDispositionToString(
61 WindowOpenDisposition window_open_disposition) {
62 switch (window_open_disposition) {
63 case IGNORE_ACTION:
64 return "ignore";
65 case SAVE_TO_DISK:
66 return "save_to_disk";
67 case CURRENT_TAB:
68 return "current_tab";
69 case NEW_BACKGROUND_TAB:
70 return "new_background_tab";
71 case NEW_FOREGROUND_TAB:
72 return "new_foreground_tab";
73 case NEW_WINDOW:
74 return "new_window";
75 case NEW_POPUP:
76 return "new_popup";
77 default:
78 NOTREACHED() << "Unknown Window Open Disposition";
79 return "ignore";
83 static std::string TerminationStatusToString(base::TerminationStatus status) {
84 switch (status) {
85 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
86 return "normal";
87 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
88 case base::TERMINATION_STATUS_STILL_RUNNING:
89 return "abnormal";
90 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
91 return "killed";
92 case base::TERMINATION_STATUS_PROCESS_CRASHED:
93 return "crashed";
94 case base::TERMINATION_STATUS_MAX_ENUM:
95 break;
97 NOTREACHED() << "Unknown Termination Status.";
98 return "unknown";
101 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
102 const std::string& partition_id = site_url.query();
103 bool persist_storage = site_url.path().find("persist") != std::string::npos;
104 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
107 void ParsePartitionParam(const base::DictionaryValue& create_params,
108 std::string* storage_partition_id,
109 bool* persist_storage) {
110 std::string partition_str;
111 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
112 return;
115 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
116 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
117 // remove the prefix without splicing in the middle of a multi-byte codepoint.
118 // We can use the rest of the string as UTF-8 encoded one.
119 if (StartsWithASCII(partition_str, "persist:", true)) {
120 size_t index = partition_str.find(":");
121 CHECK(index != std::string::npos);
122 // It is safe to do index + 1, since we tested for the full prefix above.
123 *storage_partition_id = partition_str.substr(index + 1);
125 if (storage_partition_id->empty()) {
126 // TODO(lazyboy): Better way to deal with this error.
127 return;
129 *persist_storage = true;
130 } else {
131 *storage_partition_id = partition_str;
132 *persist_storage = false;
136 void RemoveWebViewEventListenersOnIOThread(
137 void* profile,
138 const std::string& extension_id,
139 int embedder_process_id,
140 int view_instance_id) {
141 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
142 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
143 profile,
144 extension_id,
145 embedder_process_id,
146 view_instance_id);
149 } // namespace
151 // static
152 GuestViewBase* WebViewGuest::Create(content::BrowserContext* browser_context,
153 int guest_instance_id) {
154 return new WebViewGuest(browser_context, guest_instance_id);
157 // static
158 bool WebViewGuest::GetGuestPartitionConfigForSite(
159 const GURL& site,
160 std::string* partition_domain,
161 std::string* partition_name,
162 bool* in_memory) {
163 if (!site.SchemeIs(content::kGuestScheme))
164 return false;
166 // Since guest URLs are only used for packaged apps, there must be an app
167 // id in the URL.
168 CHECK(site.has_host());
169 *partition_domain = site.host();
170 // Since persistence is optional, the path must either be empty or the
171 // literal string.
172 *in_memory = (site.path() != "/persist");
173 // The partition name is user supplied value, which we have encoded when the
174 // URL was created, so it needs to be decoded.
175 *partition_name =
176 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
177 return true;
180 // static
181 const char WebViewGuest::Type[] = "webview";
183 // static
184 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
185 WebViewGuest* guest = FromWebContents(contents);
186 if (!guest)
187 return guestview::kInstanceIDNone;
189 return guest->view_instance_id();
192 const char* WebViewGuest::GetAPINamespace() const {
193 return webview::kAPINamespace;
196 int WebViewGuest::GetTaskPrefix() const {
197 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
200 void WebViewGuest::CreateWebContents(
201 const std::string& embedder_extension_id,
202 int embedder_render_process_id,
203 const GURL& embedder_site_url,
204 const base::DictionaryValue& create_params,
205 const WebContentsCreatedCallback& callback) {
206 content::RenderProcessHost* embedder_render_process_host =
207 content::RenderProcessHost::FromID(embedder_render_process_id);
208 std::string storage_partition_id;
209 bool persist_storage = false;
210 std::string storage_partition_string;
211 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
212 // Validate that the partition id coming from the renderer is valid UTF-8,
213 // since we depend on this in other parts of the code, such as FilePath
214 // creation. If the validation fails, treat it as a bad message and kill the
215 // renderer process.
216 if (!base::IsStringUTF8(storage_partition_id)) {
217 content::RecordAction(
218 base::UserMetricsAction("BadMessageTerminate_BPGM"));
219 base::KillProcess(
220 embedder_render_process_host->GetHandle(),
221 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
222 callback.Run(NULL);
223 return;
225 std::string url_encoded_partition = net::EscapeQueryParamValue(
226 storage_partition_id, false);
227 // The SiteInstance of a given webview tag is based on the fact that it's
228 // a guest process in addition to which platform application or which WebUI
229 // page the tag belongs to and what storage partition is in use, rather than
230 // the URL that the tag is being navigated to.
231 std::string partition_domain;
232 if (embedder_extension_id.empty()) {
233 DCHECK(content::ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(
234 embedder_render_process_id));
235 partition_domain = embedder_site_url.host();
236 } else {
237 partition_domain = embedder_extension_id;
239 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
240 content::kGuestScheme,
241 partition_domain.c_str(),
242 persist_storage ? "persist" : "",
243 url_encoded_partition.c_str()));
245 // If we already have a webview tag in the same app using the same storage
246 // partition, we should use the same SiteInstance so the existing tag and
247 // the new tag can script each other.
248 GuestViewManager* guest_view_manager =
249 GuestViewManager::FromBrowserContext(
250 embedder_render_process_host->GetBrowserContext());
251 content::SiteInstance* guest_site_instance =
252 guest_view_manager->GetGuestSiteInstance(guest_site);
253 if (!guest_site_instance) {
254 // Create the SiteInstance in a new BrowsingInstance, which will ensure
255 // that webview tags are also not allowed to send messages across
256 // different partitions.
257 guest_site_instance = content::SiteInstance::CreateForURL(
258 embedder_render_process_host->GetBrowserContext(), guest_site);
260 WebContents::CreateParams params(
261 embedder_render_process_host->GetBrowserContext(),
262 guest_site_instance);
263 params.guest_delegate = this;
264 callback.Run(WebContents::Create(params));
267 void WebViewGuest::DidAttachToEmbedder() {
268 SetUpAutoSize();
270 std::string name;
271 if (attach_params()->GetString(webview::kAttributeName, &name)) {
272 // If the guest window's name is empty, then the WebView tag's name is
273 // assigned. Otherwise, the guest window's name takes precedence over the
274 // WebView tag's name.
275 if (name_.empty())
276 name_ = name;
278 ReportFrameNameChange(name_);
280 std::string user_agent_override;
281 if (attach_params()->GetString(webview::kParameterUserAgentOverride,
282 &user_agent_override)) {
283 SetUserAgentOverride(user_agent_override);
284 } else {
285 SetUserAgentOverride("");
288 bool is_pending_new_window = false;
289 if (GetOpener()) {
290 // We need to do a navigation here if the target URL has changed between
291 // the time the WebContents was created and the time it was attached.
292 // We also need to do an initial navigation if a RenderView was never
293 // created for the new window in cases where there is no referrer.
294 PendingWindowMap::iterator it =
295 GetOpener()->pending_new_windows_.find(this);
296 if (it != GetOpener()->pending_new_windows_.end()) {
297 const NewWindowInfo& new_window_info = it->second;
298 if (new_window_info.changed || !web_contents()->HasOpener())
299 NavigateGuest(new_window_info.url.spec(), false /* force_navigation */);
301 // Once a new guest is attached to the DOM of the embedder page, then the
302 // lifetime of the new guest is no longer managed by the opener guest.
303 GetOpener()->pending_new_windows_.erase(this);
305 is_pending_new_window = true;
309 // Only read the src attribute if this is not a New Window API flow.
310 if (!is_pending_new_window) {
311 std::string src;
312 if (attach_params()->GetString(webview::kAttributeSrc, &src) &&
313 !src.empty()) {
314 NavigateGuest(src, false /* force_navigation */);
318 bool allow_transparency = false;
319 attach_params()->GetBoolean(webview::kAttributeAllowTransparency,
320 &allow_transparency);
321 // We need to set the background opaque flag after navigation to ensure that
322 // there is a RenderWidgetHostView available.
323 SetAllowTransparency(allow_transparency);
325 if (web_view_guest_delegate_)
326 web_view_guest_delegate_->OnDidAttachToEmbedder();
329 void WebViewGuest::DidInitialize() {
330 script_executor_.reset(
331 new ScriptExecutor(web_contents(), &script_observers_));
333 notification_registrar_.Add(this,
334 content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
335 content::Source<WebContents>(web_contents()));
337 notification_registrar_.Add(this,
338 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
339 content::Source<WebContents>(web_contents()));
341 if (web_view_guest_delegate_)
342 web_view_guest_delegate_->OnDidInitialize();
343 AttachWebViewHelpers(web_contents());
346 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) {
347 if (web_view_guest_delegate_)
348 web_view_guest_delegate_->OnAttachWebViewHelpers(contents);
349 web_view_permission_helper_.reset(new WebViewPermissionHelper(this));
352 void WebViewGuest::DidStopLoading() {
353 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
354 DispatchEventToEmbedder(
355 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
358 void WebViewGuest::EmbedderWillBeDestroyed() {
359 if (web_view_guest_delegate_)
360 web_view_guest_delegate_->OnEmbedderWillBeDestroyed();
362 content::BrowserThread::PostTask(
363 content::BrowserThread::IO,
364 FROM_HERE,
365 base::Bind(
366 &RemoveWebViewEventListenersOnIOThread,
367 browser_context(),
368 embedder_extension_id(),
369 embedder_render_process_id(),
370 view_instance_id()));
373 void WebViewGuest::GuestDestroyed() {
374 // Clean up custom context menu items for this guest.
375 if (web_view_guest_delegate_)
376 web_view_guest_delegate_->OnGuestDestroyed();
377 RemoveWebViewStateFromIOThread(web_contents());
380 void WebViewGuest::GuestReady() {
381 // The guest RenderView should always live in an isolated guest process.
382 CHECK(web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
383 Send(new ExtensionMsg_SetFrameName(web_contents()->GetRoutingID(), name_));
385 // We don't want to accidentally set the opacity of an interstitial page.
386 // WebContents::GetRenderWidgetHostView will return the RWHV of an
387 // interstitial page if one is showing at this time. We only want opacity
388 // to apply to web pages.
389 if (guest_opaque_) {
390 web_contents()
391 ->GetRenderViewHost()
392 ->GetView()
393 ->SetBackgroundColorToDefault();
394 } else {
395 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
396 SK_ColorTRANSPARENT);
400 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
401 const gfx::Size& new_size) {
402 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
403 args->SetInteger(webview::kOldHeight, old_size.height());
404 args->SetInteger(webview::kOldWidth, old_size.width());
405 args->SetInteger(webview::kNewHeight, new_size.height());
406 args->SetInteger(webview::kNewWidth, new_size.width());
407 DispatchEventToEmbedder(
408 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
411 bool WebViewGuest::IsAutoSizeSupported() const {
412 return true;
415 bool WebViewGuest::IsDragAndDropEnabled() const {
416 return true;
419 void WebViewGuest::WillDestroy() {
420 if (!attached() && GetOpener())
421 GetOpener()->pending_new_windows_.erase(this);
424 bool WebViewGuest::AddMessageToConsole(WebContents* source,
425 int32 level,
426 const base::string16& message,
427 int32 line_no,
428 const base::string16& source_id) {
429 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
430 // Log levels are from base/logging.h: LogSeverity.
431 args->SetInteger(webview::kLevel, level);
432 args->SetString(webview::kMessage, message);
433 args->SetInteger(webview::kLine, line_no);
434 args->SetString(webview::kSourceId, source_id);
435 DispatchEventToEmbedder(
436 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
437 return true;
440 void WebViewGuest::CloseContents(WebContents* source) {
441 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
442 DispatchEventToEmbedder(
443 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
446 void WebViewGuest::FindReply(WebContents* source,
447 int request_id,
448 int number_of_matches,
449 const gfx::Rect& selection_rect,
450 int active_match_ordinal,
451 bool final_update) {
452 find_helper_.FindReply(request_id,
453 number_of_matches,
454 selection_rect,
455 active_match_ordinal,
456 final_update);
459 bool WebViewGuest::HandleContextMenu(
460 const content::ContextMenuParams& params) {
461 if (!web_view_guest_delegate_)
462 return false;
463 return web_view_guest_delegate_->HandleContextMenu(params);
466 void WebViewGuest::HandleKeyboardEvent(
467 WebContents* source,
468 const content::NativeWebKeyboardEvent& event) {
469 if (!attached())
470 return;
472 if (HandleKeyboardShortcuts(event))
473 return;
475 // Send the unhandled keyboard events back to the embedder to reprocess them.
476 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
477 // events because the guest may be arbitrarily delayed when responding to
478 // keyboard events. In that time, the embedder may have received and processed
479 // additional key events. This needs to be fixed as soon as possible.
480 // See http://crbug.com/229882.
481 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
482 web_contents(), event);
485 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
486 double progress) {
487 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
488 args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
489 args->SetDouble(webview::kProgress, progress);
490 DispatchEventToEmbedder(
491 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
494 void WebViewGuest::LoadAbort(bool is_top_level,
495 const GURL& url,
496 const std::string& error_type) {
497 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
498 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
499 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
500 args->SetString(guestview::kReason, error_type);
501 DispatchEventToEmbedder(
502 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
505 void WebViewGuest::OnFrameNameChanged(bool is_top_level,
506 const std::string& name) {
507 if (!is_top_level)
508 return;
510 if (name_ == name)
511 return;
513 ReportFrameNameChange(name);
516 void WebViewGuest::CreateNewGuestWebViewWindow(
517 const content::OpenURLParams& params) {
518 GuestViewManager* guest_manager =
519 GuestViewManager::FromBrowserContext(browser_context());
520 // Set the attach params to use the same partition as the opener.
521 // We pull the partition information from the site's URL, which is of the
522 // form guest://site/{persist}?{partition_name}.
523 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
524 const std::string storage_partition_id =
525 GetStoragePartitionIdFromSiteURL(site_url);
526 base::DictionaryValue create_params;
527 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
529 guest_manager->CreateGuest(WebViewGuest::Type,
530 embedder_extension_id(),
531 embedder_web_contents(),
532 create_params,
533 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
534 weak_ptr_factory_.GetWeakPtr(),
535 params));
538 void WebViewGuest::NewGuestWebViewCallback(
539 const content::OpenURLParams& params,
540 content::WebContents* guest_web_contents) {
541 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
542 new_guest->SetOpener(this);
544 // Take ownership of |new_guest|.
545 pending_new_windows_.insert(
546 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
548 // Request permission to show the new window.
549 RequestNewWindowPermission(params.disposition,
550 gfx::Rect(),
551 params.user_gesture,
552 new_guest->web_contents());
555 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
556 // 'unresponsive' events.
557 void WebViewGuest::RendererResponsive(content::WebContents* source) {
558 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
559 args->SetInteger(webview::kProcessId,
560 web_contents()->GetRenderProcessHost()->GetID());
561 DispatchEventToEmbedder(
562 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
565 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
566 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
567 args->SetInteger(webview::kProcessId,
568 web_contents()->GetRenderProcessHost()->GetID());
569 DispatchEventToEmbedder(
570 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
573 void WebViewGuest::Observe(int type,
574 const content::NotificationSource& source,
575 const content::NotificationDetails& details) {
576 switch (type) {
577 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
578 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
579 if (content::Source<WebContents>(source).ptr() == web_contents())
580 LoadHandlerCalled();
581 break;
583 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
584 DCHECK_EQ(content::Source<WebContents>(source).ptr(), web_contents());
585 content::ResourceRedirectDetails* resource_redirect_details =
586 content::Details<content::ResourceRedirectDetails>(details).ptr();
587 bool is_top_level = resource_redirect_details->resource_type ==
588 content::RESOURCE_TYPE_MAIN_FRAME;
589 LoadRedirect(resource_redirect_details->url,
590 resource_redirect_details->new_url,
591 is_top_level);
592 break;
594 default:
595 NOTREACHED() << "Unexpected notification sent.";
596 break;
600 double WebViewGuest::GetZoom() {
601 if (!web_view_guest_delegate_)
602 return 1.0;
603 return web_view_guest_delegate_->GetZoom();
606 void WebViewGuest::StartFinding(
607 const base::string16& search_text,
608 const blink::WebFindOptions& options,
609 scoped_refptr<WebViewInternalFindFunction> find_function) {
610 find_helper_.Find(web_contents(), search_text, options, find_function);
613 void WebViewGuest::StopFinding(content::StopFindAction action) {
614 find_helper_.CancelAllFindSessions();
615 web_contents()->StopFinding(action);
618 bool WebViewGuest::Go(int relative_index) {
619 content::NavigationController& controller = web_contents()->GetController();
620 if (!controller.CanGoToOffset(relative_index))
621 return false;
623 controller.GoToOffset(relative_index);
624 return true;
627 void WebViewGuest::Reload() {
628 // TODO(fsamuel): Don't check for repost because we don't want to show
629 // Chromium's repost warning. We might want to implement a separate API
630 // for registering a callback if a repost is about to happen.
631 web_contents()->GetController().Reload(false);
634 void WebViewGuest::SetUserAgentOverride(
635 const std::string& user_agent_override) {
636 if (!attached())
637 return;
638 is_overriding_user_agent_ = !user_agent_override.empty();
639 if (is_overriding_user_agent_) {
640 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
642 web_contents()->SetUserAgentOverride(user_agent_override);
645 void WebViewGuest::Stop() {
646 web_contents()->Stop();
649 void WebViewGuest::Terminate() {
650 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
651 base::ProcessHandle process_handle =
652 web_contents()->GetRenderProcessHost()->GetHandle();
653 if (process_handle)
654 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
657 bool WebViewGuest::ClearData(const base::Time remove_since,
658 uint32 removal_mask,
659 const base::Closure& callback) {
660 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
661 content::StoragePartition* partition =
662 content::BrowserContext::GetStoragePartition(
663 web_contents()->GetBrowserContext(),
664 web_contents()->GetSiteInstance());
666 if (!partition)
667 return false;
669 partition->ClearData(
670 removal_mask,
671 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
672 GURL(),
673 content::StoragePartition::OriginMatcherFunction(),
674 remove_since,
675 base::Time::Now(),
676 callback);
677 return true;
680 WebViewGuest::WebViewGuest(content::BrowserContext* browser_context,
681 int guest_instance_id)
682 : GuestView<WebViewGuest>(browser_context, guest_instance_id),
683 find_helper_(this),
684 is_overriding_user_agent_(false),
685 guest_opaque_(true),
686 javascript_dialog_helper_(this),
687 weak_ptr_factory_(this) {
688 web_view_guest_delegate_.reset(
689 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
692 WebViewGuest::~WebViewGuest() {
695 void WebViewGuest::DidCommitProvisionalLoadForFrame(
696 content::RenderFrameHost* render_frame_host,
697 const GURL& url,
698 ui::PageTransition transition_type) {
699 if (!render_frame_host->GetParent())
700 src_ = url;
701 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
702 args->SetString(guestview::kUrl, url.spec());
703 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
704 args->SetString(webview::kInternalBaseURLForDataURL,
705 web_contents()
706 ->GetController()
707 .GetLastCommittedEntry()
708 ->GetBaseURLForDataURL()
709 .spec());
710 args->SetInteger(webview::kInternalCurrentEntryIndex,
711 web_contents()->GetController().GetCurrentEntryIndex());
712 args->SetInteger(webview::kInternalEntryCount,
713 web_contents()->GetController().GetEntryCount());
714 args->SetInteger(webview::kInternalProcessId,
715 web_contents()->GetRenderProcessHost()->GetID());
716 DispatchEventToEmbedder(
717 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
719 find_helper_.CancelAllFindSessions();
720 if (web_view_guest_delegate_) {
721 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
722 !render_frame_host->GetParent());
726 void WebViewGuest::DidFailProvisionalLoad(
727 content::RenderFrameHost* render_frame_host,
728 const GURL& validated_url,
729 int error_code,
730 const base::string16& error_description) {
731 LoadAbort(!render_frame_host->GetParent(), validated_url,
732 net::ErrorToShortString(error_code));
735 void WebViewGuest::DidStartProvisionalLoadForFrame(
736 content::RenderFrameHost* render_frame_host,
737 const GURL& validated_url,
738 bool is_error_page,
739 bool is_iframe_srcdoc) {
740 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
741 args->SetString(guestview::kUrl, validated_url.spec());
742 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
743 DispatchEventToEmbedder(
744 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
747 void WebViewGuest::DocumentLoadedInFrame(
748 content::RenderFrameHost* render_frame_host) {
749 if (web_view_guest_delegate_)
750 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
753 bool WebViewGuest::OnMessageReceived(const IPC::Message& message,
754 RenderFrameHost* render_frame_host) {
755 bool handled = true;
756 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message)
757 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged)
758 IPC_MESSAGE_UNHANDLED(handled = false)
759 IPC_END_MESSAGE_MAP()
760 return handled;
763 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
764 // Cancel all find sessions in progress.
765 find_helper_.CancelAllFindSessions();
767 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
768 args->SetInteger(webview::kProcessId,
769 web_contents()->GetRenderProcessHost()->GetID());
770 args->SetString(webview::kReason, TerminationStatusToString(status));
771 DispatchEventToEmbedder(
772 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
775 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
776 if (!attached())
777 return;
778 content::NavigationController& controller = web_contents()->GetController();
779 content::NavigationEntry* entry = controller.GetVisibleEntry();
780 if (!entry)
781 return;
782 entry->SetIsOverridingUserAgent(!user_agent.empty());
783 web_contents()->GetController().Reload(false);
786 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
787 name_ = name;
788 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
789 args->SetString(webview::kName, name);
790 DispatchEventToEmbedder(
791 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
794 void WebViewGuest::LoadHandlerCalled() {
795 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
796 DispatchEventToEmbedder(
797 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass()));
800 void WebViewGuest::LoadRedirect(const GURL& old_url,
801 const GURL& new_url,
802 bool is_top_level) {
803 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
804 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
805 args->SetString(webview::kNewURL, new_url.spec());
806 args->SetString(webview::kOldURL, old_url.spec());
807 DispatchEventToEmbedder(
808 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass()));
811 void WebViewGuest::PushWebViewStateToIOThread() {
812 const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
813 std::string partition_domain;
814 std::string partition_id;
815 bool in_memory;
816 if (!GetGuestPartitionConfigForSite(
817 site_url, &partition_domain, &partition_id, &in_memory)) {
818 NOTREACHED();
819 return;
822 WebViewRendererState::WebViewInfo web_view_info;
823 web_view_info.embedder_process_id = embedder_render_process_id();
824 web_view_info.instance_id = view_instance_id();
825 web_view_info.partition_id = partition_id;
826 web_view_info.embedder_extension_id = embedder_extension_id();
828 content::BrowserThread::PostTask(
829 content::BrowserThread::IO,
830 FROM_HERE,
831 base::Bind(&WebViewRendererState::AddGuest,
832 base::Unretained(WebViewRendererState::GetInstance()),
833 web_contents()->GetRenderProcessHost()->GetID(),
834 web_contents()->GetRoutingID(),
835 web_view_info));
838 // static
839 void WebViewGuest::RemoveWebViewStateFromIOThread(
840 WebContents* web_contents) {
841 content::BrowserThread::PostTask(
842 content::BrowserThread::IO, FROM_HERE,
843 base::Bind(
844 &WebViewRendererState::RemoveGuest,
845 base::Unretained(WebViewRendererState::GetInstance()),
846 web_contents->GetRenderProcessHost()->GetID(),
847 web_contents->GetRoutingID()));
850 content::WebContents* WebViewGuest::CreateNewGuestWindow(
851 const content::WebContents::CreateParams& create_params) {
852 GuestViewManager* guest_manager =
853 GuestViewManager::FromBrowserContext(browser_context());
854 return guest_manager->CreateGuestWithWebContentsParams(
855 WebViewGuest::Type,
856 embedder_extension_id(),
857 embedder_web_contents(),
858 create_params);
861 void WebViewGuest::RequestMediaAccessPermission(
862 content::WebContents* source,
863 const content::MediaStreamRequest& request,
864 const content::MediaResponseCallback& callback) {
865 web_view_permission_helper_->RequestMediaAccessPermission(source,
866 request,
867 callback);
870 bool WebViewGuest::CheckMediaAccessPermission(content::WebContents* source,
871 const GURL& security_origin,
872 content::MediaStreamType type) {
873 return web_view_permission_helper_->CheckMediaAccessPermission(
874 source, security_origin, type);
877 void WebViewGuest::CanDownload(
878 content::RenderViewHost* render_view_host,
879 const GURL& url,
880 const std::string& request_method,
881 const base::Callback<void(bool)>& callback) {
882 web_view_permission_helper_->CanDownload(render_view_host,
883 url,
884 request_method,
885 callback);
888 void WebViewGuest::RequestPointerLockPermission(
889 bool user_gesture,
890 bool last_unlocked_by_target,
891 const base::Callback<void(bool)>& callback) {
892 web_view_permission_helper_->RequestPointerLockPermission(
893 user_gesture,
894 last_unlocked_by_target,
895 callback);
898 void WebViewGuest::WillAttachToEmbedder() {
899 // We must install the mapping from guests to WebViews prior to resuming
900 // suspended resource loads so that the WebRequest API will catch resource
901 // requests.
902 PushWebViewStateToIOThread();
905 content::JavaScriptDialogManager* WebViewGuest::GetJavaScriptDialogManager(
906 WebContents* source) {
907 return &javascript_dialog_helper_;
910 content::ColorChooser* WebViewGuest::OpenColorChooser(
911 WebContents* web_contents,
912 SkColor color,
913 const std::vector<content::ColorSuggestion>& suggestions) {
914 if (!attached() || !embedder_web_contents()->GetDelegate())
915 return NULL;
916 return embedder_web_contents()->GetDelegate()->OpenColorChooser(
917 web_contents, color, suggestions);
920 void WebViewGuest::NavigateGuest(const std::string& src,
921 bool force_navigation) {
922 if (!attached())
923 return;
925 if (src.empty())
926 return;
928 GURL url = ResolveURL(src);
930 // Do not allow navigating a guest to schemes other than known safe schemes.
931 // This will block the embedder trying to load unwanted schemes, e.g.
932 // chrome://settings.
933 bool scheme_is_blocked =
934 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
935 url.scheme()) &&
936 !url.SchemeIs(url::kAboutScheme)) ||
937 url.SchemeIs(url::kJavaScriptScheme);
938 if (scheme_is_blocked || !url.is_valid()) {
939 LoadAbort(true /* is_top_level */, url,
940 net::ErrorToShortString(net::ERR_ABORTED));
941 return;
943 if (!force_navigation && (src_ == url))
944 return;
946 GURL validated_url(url);
947 web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
948 // As guests do not swap processes on navigation, only navigations to
949 // normal web URLs are supported. No protocol handlers are installed for
950 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
951 // can be granted to the guest process.
952 LoadURLWithParams(validated_url,
953 content::Referrer(),
954 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
955 web_contents());
958 bool WebViewGuest::HandleKeyboardShortcuts(
959 const content::NativeWebKeyboardEvent& event) {
960 if (event.type != blink::WebInputEvent::RawKeyDown)
961 return false;
963 // If the user hits the escape key without any modifiers then unlock the
964 // mouse if necessary.
965 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
966 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
967 return web_contents()->GotResponseToLockMouseRequest(false);
970 #if defined(OS_MACOSX)
971 if (event.modifiers != blink::WebInputEvent::MetaKey)
972 return false;
974 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
975 Go(-1);
976 return true;
979 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
980 Go(1);
981 return true;
983 #else
984 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
985 Go(-1);
986 return true;
989 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
990 Go(1);
991 return true;
993 #endif
995 return false;
998 void WebViewGuest::SetUpAutoSize() {
999 // Read the autosize parameters passed in from the embedder.
1000 bool auto_size_enabled = false;
1001 attach_params()->GetBoolean(webview::kAttributeAutoSize, &auto_size_enabled);
1003 int max_height = 0;
1004 int max_width = 0;
1005 attach_params()->GetInteger(webview::kAttributeMaxHeight, &max_height);
1006 attach_params()->GetInteger(webview::kAttributeMaxWidth, &max_width);
1008 int min_height = 0;
1009 int min_width = 0;
1010 attach_params()->GetInteger(webview::kAttributeMinHeight, &min_height);
1011 attach_params()->GetInteger(webview::kAttributeMinWidth, &min_width);
1013 // Call SetAutoSize to apply all the appropriate validation and clipping of
1014 // values.
1015 SetAutoSize(auto_size_enabled,
1016 gfx::Size(min_width, min_height),
1017 gfx::Size(max_width, max_height));
1020 void WebViewGuest::ShowContextMenu(
1021 int request_id,
1022 const WebViewGuestDelegate::MenuItemVector* items) {
1023 if (web_view_guest_delegate_)
1024 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
1027 void WebViewGuest::SetName(const std::string& name) {
1028 if (name_ == name)
1029 return;
1030 name_ = name;
1032 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
1035 void WebViewGuest::SetZoom(double zoom_factor) {
1036 if (web_view_guest_delegate_)
1037 web_view_guest_delegate_->OnSetZoom(zoom_factor);
1040 void WebViewGuest::SetAllowTransparency(bool allow) {
1041 if (guest_opaque_ != allow)
1042 return;
1044 guest_opaque_ = !allow;
1045 if (!web_contents()->GetRenderViewHost()->GetView())
1046 return;
1048 if (guest_opaque_) {
1049 web_contents()
1050 ->GetRenderViewHost()
1051 ->GetView()
1052 ->SetBackgroundColorToDefault();
1053 } else {
1054 web_contents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
1055 SK_ColorTRANSPARENT);
1059 bool WebViewGuest::LoadDataWithBaseURL(const std::string& data_url,
1060 const std::string& base_url,
1061 const std::string& virtual_url,
1062 std::string* error) {
1063 // Make GURLs from URLs.
1064 const GURL data_gurl = GURL(data_url);
1065 const GURL base_gurl = GURL(base_url);
1066 const GURL virtual_gurl = GURL(virtual_url);
1068 // Check that the provided URLs are valid.
1069 // |data_url| must be a valid data URL.
1070 if (!data_gurl.is_valid() || !data_gurl.SchemeIs(url::kDataScheme)) {
1071 base::SStringPrintf(
1072 error, webview::kAPILoadDataInvalidDataURL, data_url.c_str());
1073 return false;
1075 // |base_url| must be a valid URL.
1076 if (!base_gurl.is_valid()) {
1077 base::SStringPrintf(
1078 error, webview::kAPILoadDataInvalidBaseURL, base_url.c_str());
1079 return false;
1081 // |virtual_url| must be a valid URL.
1082 if (!virtual_gurl.is_valid()) {
1083 base::SStringPrintf(
1084 error, webview::kAPILoadDataInvalidVirtualURL, virtual_url.c_str());
1085 return false;
1088 // Set up the parameters to load |data_url| with the specified |base_url|.
1089 content::NavigationController::LoadURLParams load_params(data_gurl);
1090 load_params.load_type = content::NavigationController::LOAD_TYPE_DATA;
1091 load_params.base_url_for_data_url = base_gurl;
1092 load_params.virtual_url_for_data_url = virtual_gurl;
1093 load_params.override_user_agent =
1094 content::NavigationController::UA_OVERRIDE_INHERIT;
1096 // Navigate to the data URL.
1097 web_contents()->GetController().LoadURLWithParams(load_params);
1099 return true;
1102 void WebViewGuest::AddNewContents(content::WebContents* source,
1103 content::WebContents* new_contents,
1104 WindowOpenDisposition disposition,
1105 const gfx::Rect& initial_pos,
1106 bool user_gesture,
1107 bool* was_blocked) {
1108 if (was_blocked)
1109 *was_blocked = false;
1110 RequestNewWindowPermission(disposition,
1111 initial_pos,
1112 user_gesture,
1113 new_contents);
1116 content::WebContents* WebViewGuest::OpenURLFromTab(
1117 content::WebContents* source,
1118 const content::OpenURLParams& params) {
1119 // If the guest wishes to navigate away prior to attachment then we save the
1120 // navigation to perform upon attachment. Navigation initializes a lot of
1121 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
1122 // Navigation also resumes resource loading which we don't want to allow
1123 // until attachment.
1124 if (!attached()) {
1125 WebViewGuest* opener = GetOpener();
1126 PendingWindowMap::iterator it =
1127 opener->pending_new_windows_.find(this);
1128 if (it == opener->pending_new_windows_.end())
1129 return NULL;
1130 const NewWindowInfo& info = it->second;
1131 NewWindowInfo new_window_info(params.url, info.name);
1132 new_window_info.changed = new_window_info.url != info.url;
1133 it->second = new_window_info;
1134 return NULL;
1136 if (params.disposition == CURRENT_TAB) {
1137 // This can happen for cross-site redirects.
1138 LoadURLWithParams(params.url, params.referrer, params.transition, source);
1139 return source;
1142 CreateNewGuestWebViewWindow(params);
1143 return NULL;
1146 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1147 int opener_render_frame_id,
1148 const base::string16& frame_name,
1149 const GURL& target_url,
1150 content::WebContents* new_contents) {
1151 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1152 CHECK(guest);
1153 guest->SetOpener(this);
1154 std::string guest_name = base::UTF16ToUTF8(frame_name);
1155 guest->name_ = guest_name;
1156 pending_new_windows_.insert(
1157 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1160 void WebViewGuest::LoadURLWithParams(const GURL& url,
1161 const content::Referrer& referrer,
1162 ui::PageTransition transition_type,
1163 content::WebContents* web_contents) {
1164 content::NavigationController::LoadURLParams load_url_params(url);
1165 load_url_params.referrer = referrer;
1166 load_url_params.transition_type = transition_type;
1167 load_url_params.extra_headers = std::string();
1168 if (is_overriding_user_agent_) {
1169 load_url_params.override_user_agent =
1170 content::NavigationController::UA_OVERRIDE_TRUE;
1172 web_contents->GetController().LoadURLWithParams(load_url_params);
1175 void WebViewGuest::RequestNewWindowPermission(
1176 WindowOpenDisposition disposition,
1177 const gfx::Rect& initial_bounds,
1178 bool user_gesture,
1179 content::WebContents* new_contents) {
1180 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1181 if (!guest)
1182 return;
1183 PendingWindowMap::iterator it = pending_new_windows_.find(guest);
1184 if (it == pending_new_windows_.end())
1185 return;
1186 const NewWindowInfo& new_window_info = it->second;
1188 // Retrieve the opener partition info if we have it.
1189 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1190 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1192 base::DictionaryValue request_info;
1193 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1194 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1195 request_info.Set(webview::kTargetURL,
1196 new base::StringValue(new_window_info.url.spec()));
1197 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1198 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1199 // We pass in partition info so that window-s created through newwindow
1200 // API can use it to set their partition attribute.
1201 request_info.Set(webview::kStoragePartitionId,
1202 new base::StringValue(storage_partition_id));
1203 request_info.Set(
1204 webview::kWindowOpenDisposition,
1205 new base::StringValue(WindowOpenDispositionToString(disposition)));
1207 web_view_permission_helper_->
1208 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1209 request_info,
1210 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1211 weak_ptr_factory_.GetWeakPtr(),
1212 guest->guest_instance_id()),
1213 false /* allowed_by_default */);
1216 GURL WebViewGuest::ResolveURL(const std::string& src) {
1217 if (!in_extension()) {
1218 return GURL(src);
1221 GURL default_url(base::StringPrintf("%s://%s/",
1222 kExtensionScheme,
1223 embedder_extension_id().c_str()));
1224 return default_url.Resolve(src);
1227 void WebViewGuest::OnWebViewNewWindowResponse(
1228 int new_window_instance_id,
1229 bool allow,
1230 const std::string& user_input) {
1231 WebViewGuest* guest =
1232 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id);
1233 if (!guest)
1234 return;
1236 if (!allow)
1237 guest->Destroy();
1240 } // namespace extensions