Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / extensions / browser / guest_view / web_view / web_view_guest.cc
blob2276835350e56da06c1d875d94c6a403f991f06c
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/resource_request_details.h"
22 #include "content/public/browser/site_instance.h"
23 #include "content/public/browser/storage_partition.h"
24 #include "content/public/browser/user_metrics.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "content/public/common/media_stream_request.h"
28 #include "content/public/common/page_zoom.h"
29 #include "content/public/common/result_codes.h"
30 #include "content/public/common/stop_find_action.h"
31 #include "content/public/common/url_constants.h"
32 #include "extensions/browser/api/extensions_api_client.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/guest_view/guest_view_constants.h"
35 #include "extensions/browser/guest_view/guest_view_manager.h"
36 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
37 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
38 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h"
39 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
40 #include "extensions/common/constants.h"
41 #include "extensions/common/extension_messages.h"
42 #include "extensions/strings/grit/extensions_strings.h"
43 #include "ipc/ipc_message_macros.h"
44 #include "net/base/escape.h"
45 #include "net/base/net_errors.h"
46 #include "ui/base/models/simple_menu_model.h"
48 using base::UserMetricsAction;
49 using content::RenderFrameHost;
50 using content::ResourceType;
51 using content::WebContents;
53 namespace extensions {
55 namespace {
57 std::string WindowOpenDispositionToString(
58 WindowOpenDisposition window_open_disposition) {
59 switch (window_open_disposition) {
60 case IGNORE_ACTION:
61 return "ignore";
62 case SAVE_TO_DISK:
63 return "save_to_disk";
64 case CURRENT_TAB:
65 return "current_tab";
66 case NEW_BACKGROUND_TAB:
67 return "new_background_tab";
68 case NEW_FOREGROUND_TAB:
69 return "new_foreground_tab";
70 case NEW_WINDOW:
71 return "new_window";
72 case NEW_POPUP:
73 return "new_popup";
74 default:
75 NOTREACHED() << "Unknown Window Open Disposition";
76 return "ignore";
80 static std::string TerminationStatusToString(base::TerminationStatus status) {
81 switch (status) {
82 case base::TERMINATION_STATUS_NORMAL_TERMINATION:
83 return "normal";
84 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
85 case base::TERMINATION_STATUS_STILL_RUNNING:
86 return "abnormal";
87 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
88 return "killed";
89 case base::TERMINATION_STATUS_PROCESS_CRASHED:
90 return "crashed";
91 case base::TERMINATION_STATUS_MAX_ENUM:
92 break;
94 NOTREACHED() << "Unknown Termination Status.";
95 return "unknown";
98 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) {
99 const std::string& partition_id = site_url.query();
100 bool persist_storage = site_url.path().find("persist") != std::string::npos;
101 return (persist_storage ? webview::kPersistPrefix : "") + partition_id;
104 void ParsePartitionParam(const base::DictionaryValue& create_params,
105 std::string* storage_partition_id,
106 bool* persist_storage) {
107 std::string partition_str;
108 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) {
109 return;
112 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
113 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
114 // remove the prefix without splicing in the middle of a multi-byte codepoint.
115 // We can use the rest of the string as UTF-8 encoded one.
116 if (StartsWithASCII(partition_str, "persist:", true)) {
117 size_t index = partition_str.find(":");
118 CHECK(index != std::string::npos);
119 // It is safe to do index + 1, since we tested for the full prefix above.
120 *storage_partition_id = partition_str.substr(index + 1);
122 if (storage_partition_id->empty()) {
123 // TODO(lazyboy): Better way to deal with this error.
124 return;
126 *persist_storage = true;
127 } else {
128 *storage_partition_id = partition_str;
129 *persist_storage = false;
133 } // namespace
135 // static
136 GuestViewBase* WebViewGuest::Create(content::BrowserContext* browser_context,
137 int guest_instance_id) {
138 return new WebViewGuest(browser_context, guest_instance_id);
141 // static
142 bool WebViewGuest::GetGuestPartitionConfigForSite(
143 const GURL& site,
144 std::string* partition_domain,
145 std::string* partition_name,
146 bool* in_memory) {
147 if (!site.SchemeIs(content::kGuestScheme))
148 return false;
150 // Since guest URLs are only used for packaged apps, there must be an app
151 // id in the URL.
152 CHECK(site.has_host());
153 *partition_domain = site.host();
154 // Since persistence is optional, the path must either be empty or the
155 // literal string.
156 *in_memory = (site.path() != "/persist");
157 // The partition name is user supplied value, which we have encoded when the
158 // URL was created, so it needs to be decoded.
159 *partition_name =
160 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL);
161 return true;
164 // static
165 const char WebViewGuest::Type[] = "webview";
167 // static
168 int WebViewGuest::GetViewInstanceId(WebContents* contents) {
169 WebViewGuest* guest = FromWebContents(contents);
170 if (!guest)
171 return guestview::kInstanceIDNone;
173 return guest->view_instance_id();
176 const char* WebViewGuest::GetAPINamespace() const {
177 return webview::kAPINamespace;
180 int WebViewGuest::GetTaskPrefix() const {
181 return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
184 void WebViewGuest::CreateWebContents(
185 const std::string& embedder_extension_id,
186 int embedder_render_process_id,
187 const base::DictionaryValue& create_params,
188 const WebContentsCreatedCallback& callback) {
189 content::RenderProcessHost* embedder_render_process_host =
190 content::RenderProcessHost::FromID(embedder_render_process_id);
191 std::string storage_partition_id;
192 bool persist_storage = false;
193 std::string storage_partition_string;
194 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage);
195 // Validate that the partition id coming from the renderer is valid UTF-8,
196 // since we depend on this in other parts of the code, such as FilePath
197 // creation. If the validation fails, treat it as a bad message and kill the
198 // renderer process.
199 if (!base::IsStringUTF8(storage_partition_id)) {
200 content::RecordAction(
201 base::UserMetricsAction("BadMessageTerminate_BPGM"));
202 base::KillProcess(
203 embedder_render_process_host->GetHandle(),
204 content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
205 callback.Run(NULL);
206 return;
208 std::string url_encoded_partition = net::EscapeQueryParamValue(
209 storage_partition_id, false);
210 // The SiteInstance of a given webview tag is based on the fact that it's
211 // a guest process in addition to which platform application the tag
212 // belongs to and what storage partition is in use, rather than the URL
213 // that the tag is being navigated to.
214 GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
215 content::kGuestScheme,
216 embedder_extension_id.c_str(),
217 persist_storage ? "persist" : "",
218 url_encoded_partition.c_str()));
220 // If we already have a webview tag in the same app using the same storage
221 // partition, we should use the same SiteInstance so the existing tag and
222 // the new tag can script each other.
223 GuestViewManager* guest_view_manager =
224 GuestViewManager::FromBrowserContext(
225 embedder_render_process_host->GetBrowserContext());
226 content::SiteInstance* guest_site_instance =
227 guest_view_manager->GetGuestSiteInstance(guest_site);
228 if (!guest_site_instance) {
229 // Create the SiteInstance in a new BrowsingInstance, which will ensure
230 // that webview tags are also not allowed to send messages across
231 // different partitions.
232 guest_site_instance = content::SiteInstance::CreateForURL(
233 embedder_render_process_host->GetBrowserContext(), guest_site);
235 WebContents::CreateParams params(
236 embedder_render_process_host->GetBrowserContext(),
237 guest_site_instance);
238 params.guest_delegate = this;
239 callback.Run(WebContents::Create(params));
242 void WebViewGuest::DidAttachToEmbedder() {
243 SetUpAutoSize();
245 std::string name;
246 if (attach_params()->GetString(webview::kName, &name)) {
247 // If the guest window's name is empty, then the WebView tag's name is
248 // assigned. Otherwise, the guest window's name takes precedence over the
249 // WebView tag's name.
250 if (name_.empty())
251 name_ = name;
253 ReportFrameNameChange(name_);
255 std::string user_agent_override;
256 if (attach_params()->GetString(webview::kParameterUserAgentOverride,
257 &user_agent_override)) {
258 SetUserAgentOverride(user_agent_override);
259 } else {
260 SetUserAgentOverride("");
263 std::string src;
264 if (attach_params()->GetString("src", &src) && !src.empty())
265 NavigateGuest(src);
267 if (GetOpener()) {
268 // We need to do a navigation here if the target URL has changed between
269 // the time the WebContents was created and the time it was attached.
270 // We also need to do an initial navigation if a RenderView was never
271 // created for the new window in cases where there is no referrer.
272 PendingWindowMap::iterator it =
273 GetOpener()->pending_new_windows_.find(this);
274 if (it != GetOpener()->pending_new_windows_.end()) {
275 const NewWindowInfo& new_window_info = it->second;
276 if (new_window_info.changed || !guest_web_contents()->HasOpener())
277 NavigateGuest(new_window_info.url.spec());
278 } else {
279 NOTREACHED();
282 // Once a new guest is attached to the DOM of the embedder page, then the
283 // lifetime of the new guest is no longer managed by the opener guest.
284 GetOpener()->pending_new_windows_.erase(this);
288 void WebViewGuest::DidInitialize() {
289 script_executor_.reset(
290 new ScriptExecutor(guest_web_contents(), &script_observers_));
292 notification_registrar_.Add(
293 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
294 content::Source<WebContents>(guest_web_contents()));
296 notification_registrar_.Add(
297 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
298 content::Source<WebContents>(guest_web_contents()));
300 if (web_view_guest_delegate_)
301 web_view_guest_delegate_->OnDidInitialize();
302 AttachWebViewHelpers(guest_web_contents());
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 DispatchEventToEmbedder(
314 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass()));
317 void WebViewGuest::EmbedderDestroyed() {
318 if (web_view_guest_delegate_)
319 web_view_guest_delegate_->OnEmbedderDestroyed();
322 void WebViewGuest::GuestDestroyed() {
323 // Clean up custom context menu items for this guest.
324 if (web_view_guest_delegate_)
325 web_view_guest_delegate_->OnGuestDestroyed();
326 RemoveWebViewStateFromIOThread(web_contents());
329 void WebViewGuest::GuestReady() {
330 // The guest RenderView should always live in an isolated guest process.
331 CHECK(guest_web_contents()->GetRenderProcessHost()->IsIsolatedGuest());
332 Send(new ExtensionMsg_SetFrameName(
333 guest_web_contents()->GetRoutingID(), name_));
336 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
337 const gfx::Size& new_size) {
338 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
339 args->SetInteger(webview::kOldHeight, old_size.height());
340 args->SetInteger(webview::kOldWidth, old_size.width());
341 args->SetInteger(webview::kNewHeight, new_size.height());
342 args->SetInteger(webview::kNewWidth, new_size.width());
343 DispatchEventToEmbedder(
344 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass()));
347 bool WebViewGuest::IsAutoSizeSupported() const {
348 return true;
351 bool WebViewGuest::IsDragAndDropEnabled() const {
352 return true;
355 void WebViewGuest::WillDestroy() {
356 if (!attached() && GetOpener())
357 GetOpener()->pending_new_windows_.erase(this);
358 DestroyUnattachedWindows();
360 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
361 DispatchEventToEmbedder(
362 new GuestViewBase::Event(webview::kEventPluginDestroyed, args.Pass()));
365 bool WebViewGuest::AddMessageToConsole(WebContents* source,
366 int32 level,
367 const base::string16& message,
368 int32 line_no,
369 const base::string16& source_id) {
370 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
371 // Log levels are from base/logging.h: LogSeverity.
372 args->SetInteger(webview::kLevel, level);
373 args->SetString(webview::kMessage, message);
374 args->SetInteger(webview::kLine, line_no);
375 args->SetString(webview::kSourceId, source_id);
376 DispatchEventToEmbedder(
377 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass()));
378 return true;
381 void WebViewGuest::CloseContents(WebContents* source) {
382 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
383 DispatchEventToEmbedder(
384 new GuestViewBase::Event(webview::kEventClose, args.Pass()));
387 void WebViewGuest::FindReply(WebContents* source,
388 int request_id,
389 int number_of_matches,
390 const gfx::Rect& selection_rect,
391 int active_match_ordinal,
392 bool final_update) {
393 if (web_view_guest_delegate_) {
394 web_view_guest_delegate_->FindReply(
395 source, request_id, number_of_matches,
396 selection_rect, active_match_ordinal, final_update);
400 bool WebViewGuest::HandleContextMenu(
401 const content::ContextMenuParams& params) {
402 if (!web_view_guest_delegate_)
403 return false;
404 return web_view_guest_delegate_->HandleContextMenu(params);
407 void WebViewGuest::HandleKeyboardEvent(
408 WebContents* source,
409 const content::NativeWebKeyboardEvent& event) {
410 if (!attached())
411 return;
413 if (HandleKeyboardShortcuts(event))
414 return;
416 // Send the unhandled keyboard events back to the embedder to reprocess them.
417 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
418 // events because the guest may be arbitrarily delayed when responding to
419 // keyboard events. In that time, the embedder may have received and processed
420 // additional key events. This needs to be fixed as soon as possible.
421 // See http://crbug.com/229882.
422 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
423 web_contents(), event);
426 void WebViewGuest::LoadProgressChanged(content::WebContents* source,
427 double progress) {
428 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
429 args->SetString(guestview::kUrl, guest_web_contents()->GetURL().spec());
430 args->SetDouble(webview::kProgress, progress);
431 DispatchEventToEmbedder(
432 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass()));
435 void WebViewGuest::LoadAbort(bool is_top_level,
436 const GURL& url,
437 const std::string& error_type) {
438 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
439 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
440 args->SetString(guestview::kUrl, url.possibly_invalid_spec());
441 args->SetString(guestview::kReason, error_type);
442 DispatchEventToEmbedder(
443 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass()));
446 void WebViewGuest::OnFrameNameChanged(bool is_top_level,
447 const std::string& name) {
448 if (!is_top_level)
449 return;
451 if (name_ == name)
452 return;
454 ReportFrameNameChange(name);
457 void WebViewGuest::CreateNewGuestWebViewWindow(
458 const content::OpenURLParams& params) {
459 GuestViewManager* guest_manager =
460 GuestViewManager::FromBrowserContext(browser_context());
461 // Set the attach params to use the same partition as the opener.
462 // We pull the partition information from the site's URL, which is of the
463 // form guest://site/{persist}?{partition_name}.
464 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL();
465 const std::string storage_partition_id =
466 GetStoragePartitionIdFromSiteURL(site_url);
467 base::DictionaryValue create_params;
468 create_params.SetString(webview::kStoragePartitionId, storage_partition_id);
470 guest_manager->CreateGuest(WebViewGuest::Type,
471 embedder_extension_id(),
472 embedder_web_contents(),
473 create_params,
474 base::Bind(&WebViewGuest::NewGuestWebViewCallback,
475 base::Unretained(this),
476 params));
479 void WebViewGuest::NewGuestWebViewCallback(
480 const content::OpenURLParams& params,
481 content::WebContents* guest_web_contents) {
482 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents);
483 new_guest->SetOpener(this);
485 // Take ownership of |new_guest|.
486 pending_new_windows_.insert(
487 std::make_pair(new_guest, NewWindowInfo(params.url, std::string())));
489 // Request permission to show the new window.
490 RequestNewWindowPermission(params.disposition, gfx::Rect(),
491 params.user_gesture,
492 new_guest->guest_web_contents());
495 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
496 // 'unresponsive' events.
497 void WebViewGuest::RendererResponsive(content::WebContents* source) {
498 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
499 args->SetInteger(webview::kProcessId,
500 guest_web_contents()->GetRenderProcessHost()->GetID());
501 DispatchEventToEmbedder(
502 new GuestViewBase::Event(webview::kEventResponsive, args.Pass()));
505 void WebViewGuest::RendererUnresponsive(content::WebContents* source) {
506 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
507 args->SetInteger(webview::kProcessId,
508 guest_web_contents()->GetRenderProcessHost()->GetID());
509 DispatchEventToEmbedder(
510 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass()));
513 void WebViewGuest::Observe(int type,
514 const content::NotificationSource& source,
515 const content::NotificationDetails& details) {
516 switch (type) {
517 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
518 DCHECK_EQ(content::Source<WebContents>(source).ptr(),
519 guest_web_contents());
520 if (content::Source<WebContents>(source).ptr() == guest_web_contents())
521 LoadHandlerCalled();
522 break;
524 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
525 DCHECK_EQ(content::Source<WebContents>(source).ptr(),
526 guest_web_contents());
527 content::ResourceRedirectDetails* resource_redirect_details =
528 content::Details<content::ResourceRedirectDetails>(details).ptr();
529 bool is_top_level = resource_redirect_details->resource_type ==
530 content::RESOURCE_TYPE_MAIN_FRAME;
531 LoadRedirect(resource_redirect_details->url,
532 resource_redirect_details->new_url,
533 is_top_level);
534 break;
536 default:
537 NOTREACHED() << "Unexpected notification sent.";
538 break;
542 double WebViewGuest::GetZoom() {
543 if (!web_view_guest_delegate_)
544 return 1.0;
545 return web_view_guest_delegate_->GetZoom();
548 void WebViewGuest::Find(
549 const base::string16& search_text,
550 const blink::WebFindOptions& options,
551 WebViewInternalFindFunction* find_function) {
552 if (web_view_guest_delegate_)
553 web_view_guest_delegate_->Find(search_text, options, find_function);
556 void WebViewGuest::StopFinding(content::StopFindAction action) {
557 if (web_view_guest_delegate_)
558 web_view_guest_delegate_->StopFinding(action);
561 void WebViewGuest::Go(int relative_index) {
562 guest_web_contents()->GetController().GoToOffset(relative_index);
565 void WebViewGuest::Reload() {
566 // TODO(fsamuel): Don't check for repost because we don't want to show
567 // Chromium's repost warning. We might want to implement a separate API
568 // for registering a callback if a repost is about to happen.
569 guest_web_contents()->GetController().Reload(false);
572 void WebViewGuest::SetUserAgentOverride(
573 const std::string& user_agent_override) {
574 if (!attached())
575 return;
576 is_overriding_user_agent_ = !user_agent_override.empty();
577 if (is_overriding_user_agent_) {
578 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
580 guest_web_contents()->SetUserAgentOverride(user_agent_override);
583 void WebViewGuest::Stop() {
584 guest_web_contents()->Stop();
587 void WebViewGuest::Terminate() {
588 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
589 base::ProcessHandle process_handle =
590 guest_web_contents()->GetRenderProcessHost()->GetHandle();
591 if (process_handle)
592 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
595 bool WebViewGuest::ClearData(const base::Time remove_since,
596 uint32 removal_mask,
597 const base::Closure& callback) {
598 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
599 content::StoragePartition* partition =
600 content::BrowserContext::GetStoragePartition(
601 guest_web_contents()->GetBrowserContext(),
602 guest_web_contents()->GetSiteInstance());
604 if (!partition)
605 return false;
607 partition->ClearData(
608 removal_mask,
609 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
610 GURL(),
611 content::StoragePartition::OriginMatcherFunction(),
612 remove_since,
613 base::Time::Now(),
614 callback);
615 return true;
618 WebViewGuest::WebViewGuest(content::BrowserContext* browser_context,
619 int guest_instance_id)
620 : GuestView<WebViewGuest>(browser_context, guest_instance_id),
621 is_overriding_user_agent_(false),
622 javascript_dialog_helper_(this) {
623 web_view_guest_delegate_.reset(
624 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this));
627 WebViewGuest::~WebViewGuest() {
630 void WebViewGuest::DidCommitProvisionalLoadForFrame(
631 content::RenderFrameHost* render_frame_host,
632 const GURL& url,
633 content::PageTransition transition_type) {
634 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
635 args->SetString(guestview::kUrl, url.spec());
636 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
637 args->SetInteger(webview::kInternalCurrentEntryIndex,
638 guest_web_contents()->GetController().GetCurrentEntryIndex());
639 args->SetInteger(webview::kInternalEntryCount,
640 guest_web_contents()->GetController().GetEntryCount());
641 args->SetInteger(webview::kInternalProcessId,
642 guest_web_contents()->GetRenderProcessHost()->GetID());
643 DispatchEventToEmbedder(
644 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass()));
645 if (web_view_guest_delegate_) {
646 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame(
647 !render_frame_host->GetParent());
651 void WebViewGuest::DidFailProvisionalLoad(
652 content::RenderFrameHost* render_frame_host,
653 const GURL& validated_url,
654 int error_code,
655 const base::string16& error_description) {
656 LoadAbort(!render_frame_host->GetParent(), validated_url,
657 net::ErrorToShortString(error_code));
660 void WebViewGuest::DidStartProvisionalLoadForFrame(
661 content::RenderFrameHost* render_frame_host,
662 const GURL& validated_url,
663 bool is_error_page,
664 bool is_iframe_srcdoc) {
665 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
666 args->SetString(guestview::kUrl, validated_url.spec());
667 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent());
668 DispatchEventToEmbedder(
669 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass()));
672 void WebViewGuest::DocumentLoadedInFrame(
673 content::RenderFrameHost* render_frame_host) {
674 if (web_view_guest_delegate_)
675 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host);
678 bool WebViewGuest::OnMessageReceived(const IPC::Message& message,
679 RenderFrameHost* render_frame_host) {
680 bool handled = true;
681 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message)
682 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged)
683 IPC_MESSAGE_UNHANDLED(handled = false)
684 IPC_END_MESSAGE_MAP()
685 return handled;
688 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) {
689 if (web_view_guest_delegate_)
690 web_view_guest_delegate_->OnRenderProcessGone();
692 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
693 args->SetInteger(webview::kProcessId,
694 guest_web_contents()->GetRenderProcessHost()->GetID());
695 args->SetString(webview::kReason, TerminationStatusToString(status));
696 DispatchEventToEmbedder(
697 new GuestViewBase::Event(webview::kEventExit, args.Pass()));
700 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
701 if (!attached())
702 return;
703 content::NavigationController& controller =
704 guest_web_contents()->GetController();
705 content::NavigationEntry* entry = controller.GetVisibleEntry();
706 if (!entry)
707 return;
708 entry->SetIsOverridingUserAgent(!user_agent.empty());
709 guest_web_contents()->GetController().Reload(false);
712 void WebViewGuest::ReportFrameNameChange(const std::string& name) {
713 name_ = name;
714 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
715 args->SetString(webview::kName, name);
716 DispatchEventToEmbedder(
717 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass()));
720 void WebViewGuest::LoadHandlerCalled() {
721 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
722 DispatchEventToEmbedder(
723 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass()));
726 void WebViewGuest::LoadRedirect(const GURL& old_url,
727 const GURL& new_url,
728 bool is_top_level) {
729 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
730 args->SetBoolean(guestview::kIsTopLevel, is_top_level);
731 args->SetString(webview::kNewURL, new_url.spec());
732 args->SetString(webview::kOldURL, old_url.spec());
733 DispatchEventToEmbedder(
734 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass()));
737 void WebViewGuest::PushWebViewStateToIOThread() {
738 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL();
739 std::string partition_domain;
740 std::string partition_id;
741 bool in_memory;
742 if (!GetGuestPartitionConfigForSite(
743 site_url, &partition_domain, &partition_id, &in_memory)) {
744 NOTREACHED();
745 return;
747 DCHECK(embedder_extension_id() == partition_domain);
749 WebViewRendererState::WebViewInfo web_view_info;
750 web_view_info.embedder_process_id = embedder_render_process_id();
751 web_view_info.instance_id = view_instance_id();
752 web_view_info.partition_id = partition_id;
753 web_view_info.embedder_extension_id = embedder_extension_id();
755 content::BrowserThread::PostTask(
756 content::BrowserThread::IO,
757 FROM_HERE,
758 base::Bind(&WebViewRendererState::AddGuest,
759 base::Unretained(WebViewRendererState::GetInstance()),
760 guest_web_contents()->GetRenderProcessHost()->GetID(),
761 guest_web_contents()->GetRoutingID(),
762 web_view_info));
765 // static
766 void WebViewGuest::RemoveWebViewStateFromIOThread(
767 WebContents* web_contents) {
768 content::BrowserThread::PostTask(
769 content::BrowserThread::IO, FROM_HERE,
770 base::Bind(
771 &WebViewRendererState::RemoveGuest,
772 base::Unretained(WebViewRendererState::GetInstance()),
773 web_contents->GetRenderProcessHost()->GetID(),
774 web_contents->GetRoutingID()));
777 content::WebContents* WebViewGuest::CreateNewGuestWindow(
778 const content::WebContents::CreateParams& create_params) {
779 GuestViewManager* guest_manager =
780 GuestViewManager::FromBrowserContext(browser_context());
781 return guest_manager->CreateGuestWithWebContentsParams(
782 WebViewGuest::Type,
783 embedder_extension_id(),
784 embedder_web_contents()->GetRenderProcessHost()->GetID(),
785 create_params);
788 void WebViewGuest::RequestMediaAccessPermission(
789 content::WebContents* source,
790 const content::MediaStreamRequest& request,
791 const content::MediaResponseCallback& callback) {
792 web_view_permission_helper_->RequestMediaAccessPermission(source,
793 request,
794 callback);
797 void WebViewGuest::CanDownload(
798 content::RenderViewHost* render_view_host,
799 const GURL& url,
800 const std::string& request_method,
801 const base::Callback<void(bool)>& callback) {
802 web_view_permission_helper_->CanDownload(render_view_host,
803 url,
804 request_method,
805 callback);
808 void WebViewGuest::RequestPointerLockPermission(
809 bool user_gesture,
810 bool last_unlocked_by_target,
811 const base::Callback<void(bool)>& callback) {
812 web_view_permission_helper_->RequestPointerLockPermission(
813 user_gesture,
814 last_unlocked_by_target,
815 callback);
818 void WebViewGuest::WillAttachToEmbedder() {
819 // We must install the mapping from guests to WebViews prior to resuming
820 // suspended resource loads so that the WebRequest API will catch resource
821 // requests.
822 PushWebViewStateToIOThread();
825 content::JavaScriptDialogManager*
826 WebViewGuest::GetJavaScriptDialogManager() {
827 return &javascript_dialog_helper_;
830 content::ColorChooser* WebViewGuest::OpenColorChooser(
831 WebContents* web_contents,
832 SkColor color,
833 const std::vector<content::ColorSuggestion>& suggestions) {
834 if (!attached() || !embedder_web_contents()->GetDelegate())
835 return NULL;
836 return embedder_web_contents()->GetDelegate()->OpenColorChooser(
837 web_contents, color, suggestions);
840 void WebViewGuest::RunFileChooser(WebContents* web_contents,
841 const content::FileChooserParams& params) {
842 if (!attached() || !embedder_web_contents()->GetDelegate())
843 return;
845 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
848 void WebViewGuest::NavigateGuest(const std::string& src) {
849 GURL url = ResolveURL(src);
851 // Do not allow navigating a guest to schemes other than known safe schemes.
852 // This will block the embedder trying to load unwanted schemes, e.g.
853 // chrome://settings.
854 bool scheme_is_blocked =
855 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
856 url.scheme()) &&
857 !url.SchemeIs(url::kAboutScheme)) ||
858 url.SchemeIs(url::kJavaScriptScheme);
859 if (scheme_is_blocked || !url.is_valid()) {
860 LoadAbort(true /* is_top_level */, url,
861 net::ErrorToShortString(net::ERR_ABORTED));
862 return;
865 GURL validated_url(url);
866 guest_web_contents()->GetRenderProcessHost()->
867 FilterURL(false, &validated_url);
868 // As guests do not swap processes on navigation, only navigations to
869 // normal web URLs are supported. No protocol handlers are installed for
870 // other schemes (e.g., WebUI or extensions), and no permissions or bindings
871 // can be granted to the guest process.
872 LoadURLWithParams(validated_url,
873 content::Referrer(),
874 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
875 guest_web_contents());
878 bool WebViewGuest::HandleKeyboardShortcuts(
879 const content::NativeWebKeyboardEvent& event) {
880 if (event.type != blink::WebInputEvent::RawKeyDown)
881 return false;
883 // If the user hits the escape key without any modifiers then unlock the
884 // mouse if necessary.
885 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) &&
886 !(event.modifiers & blink::WebInputEvent::InputModifiers)) {
887 return guest_web_contents()->GotResponseToLockMouseRequest(false);
890 #if defined(OS_MACOSX)
891 if (event.modifiers != blink::WebInputEvent::MetaKey)
892 return false;
894 if (event.windowsKeyCode == ui::VKEY_OEM_4) {
895 Go(-1);
896 return true;
899 if (event.windowsKeyCode == ui::VKEY_OEM_6) {
900 Go(1);
901 return true;
903 #else
904 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
905 Go(-1);
906 return true;
909 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
910 Go(1);
911 return true;
913 #endif
915 return false;
918 void WebViewGuest::SetUpAutoSize() {
919 // Read the autosize parameters passed in from the embedder.
920 bool auto_size_enabled = false;
921 attach_params()->GetBoolean(webview::kAttributeAutoSize, &auto_size_enabled);
923 int max_height = 0;
924 int max_width = 0;
925 attach_params()->GetInteger(webview::kAttributeMaxHeight, &max_height);
926 attach_params()->GetInteger(webview::kAttributeMaxWidth, &max_width);
928 int min_height = 0;
929 int min_width = 0;
930 attach_params()->GetInteger(webview::kAttributeMinHeight, &min_height);
931 attach_params()->GetInteger(webview::kAttributeMinWidth, &min_width);
933 // Call SetAutoSize to apply all the appropriate validation and clipping of
934 // values.
935 SetAutoSize(auto_size_enabled,
936 gfx::Size(min_width, min_height),
937 gfx::Size(max_width, max_height));
940 void WebViewGuest::ShowContextMenu(
941 int request_id,
942 const WebViewGuestDelegate::MenuItemVector* items) {
943 if (web_view_guest_delegate_)
944 web_view_guest_delegate_->OnShowContextMenu(request_id, items);
947 void WebViewGuest::SetName(const std::string& name) {
948 if (name_ == name)
949 return;
950 name_ = name;
952 Send(new ExtensionMsg_SetFrameName(routing_id(), name_));
955 void WebViewGuest::SetZoom(double zoom_factor) {
956 if (web_view_guest_delegate_)
957 web_view_guest_delegate_->OnSetZoom(zoom_factor);
960 void WebViewGuest::AddNewContents(content::WebContents* source,
961 content::WebContents* new_contents,
962 WindowOpenDisposition disposition,
963 const gfx::Rect& initial_pos,
964 bool user_gesture,
965 bool* was_blocked) {
966 if (was_blocked)
967 *was_blocked = false;
968 RequestNewWindowPermission(disposition,
969 initial_pos,
970 user_gesture,
971 new_contents);
974 content::WebContents* WebViewGuest::OpenURLFromTab(
975 content::WebContents* source,
976 const content::OpenURLParams& params) {
977 // If the guest wishes to navigate away prior to attachment then we save the
978 // navigation to perform upon attachment. Navigation initializes a lot of
979 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
980 // Navigation also resumes resource loading which we don't want to allow
981 // until attachment.
982 if (!attached()) {
983 WebViewGuest* opener = GetOpener();
984 PendingWindowMap::iterator it =
985 opener->pending_new_windows_.find(this);
986 if (it == opener->pending_new_windows_.end())
987 return NULL;
988 const NewWindowInfo& info = it->second;
989 NewWindowInfo new_window_info(params.url, info.name);
990 new_window_info.changed = new_window_info.url != info.url;
991 it->second = new_window_info;
992 return NULL;
994 if (params.disposition == CURRENT_TAB) {
995 // This can happen for cross-site redirects.
996 LoadURLWithParams(params.url, params.referrer, params.transition, source);
997 return source;
1000 CreateNewGuestWebViewWindow(params);
1001 return NULL;
1004 void WebViewGuest::WebContentsCreated(WebContents* source_contents,
1005 int opener_render_frame_id,
1006 const base::string16& frame_name,
1007 const GURL& target_url,
1008 content::WebContents* new_contents) {
1009 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1010 CHECK(guest);
1011 guest->SetOpener(this);
1012 std::string guest_name = base::UTF16ToUTF8(frame_name);
1013 guest->name_ = guest_name;
1014 pending_new_windows_.insert(
1015 std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
1018 void WebViewGuest::LoadURLWithParams(const GURL& url,
1019 const content::Referrer& referrer,
1020 content::PageTransition transition_type,
1021 content::WebContents* web_contents) {
1022 content::NavigationController::LoadURLParams load_url_params(url);
1023 load_url_params.referrer = referrer;
1024 load_url_params.transition_type = transition_type;
1025 load_url_params.extra_headers = std::string();
1026 if (is_overriding_user_agent_) {
1027 load_url_params.override_user_agent =
1028 content::NavigationController::UA_OVERRIDE_TRUE;
1030 web_contents->GetController().LoadURLWithParams(load_url_params);
1033 void WebViewGuest::RequestNewWindowPermission(
1034 WindowOpenDisposition disposition,
1035 const gfx::Rect& initial_bounds,
1036 bool user_gesture,
1037 content::WebContents* new_contents) {
1038 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents);
1039 if (!guest)
1040 return;
1041 PendingWindowMap::iterator it = pending_new_windows_.find(guest);
1042 if (it == pending_new_windows_.end())
1043 return;
1044 const NewWindowInfo& new_window_info = it->second;
1046 // Retrieve the opener partition info if we have it.
1047 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL();
1048 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url);
1050 base::DictionaryValue request_info;
1051 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height());
1052 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width());
1053 request_info.Set(webview::kTargetURL,
1054 new base::StringValue(new_window_info.url.spec()));
1055 request_info.Set(webview::kName, new base::StringValue(new_window_info.name));
1056 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id());
1057 // We pass in partition info so that window-s created through newwindow
1058 // API can use it to set their partition attribute.
1059 request_info.Set(webview::kStoragePartitionId,
1060 new base::StringValue(storage_partition_id));
1061 request_info.Set(
1062 webview::kWindowOpenDisposition,
1063 new base::StringValue(WindowOpenDispositionToString(disposition)));
1065 web_view_permission_helper_->
1066 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW,
1067 request_info,
1068 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse,
1069 base::Unretained(this),
1070 guest->guest_instance_id()),
1071 false /* allowed_by_default */);
1074 void WebViewGuest::DestroyUnattachedWindows() {
1075 // Destroy() reaches in and removes the WebViewGuest from its opener's
1076 // pending_new_windows_ set. To avoid mutating the set while iterating, we
1077 // create a copy of the pending new windows set and iterate over the copy.
1078 PendingWindowMap pending_new_windows(pending_new_windows_);
1079 // Clean up unattached new windows opened by this guest.
1080 for (PendingWindowMap::const_iterator it = pending_new_windows.begin();
1081 it != pending_new_windows.end(); ++it) {
1082 it->first->Destroy();
1084 // All pending windows should be removed from the set after Destroy() is
1085 // called on all of them.
1086 DCHECK(pending_new_windows_.empty());
1089 GURL WebViewGuest::ResolveURL(const std::string& src) {
1090 if (!in_extension()) {
1091 NOTREACHED();
1092 return GURL(src);
1095 GURL default_url(base::StringPrintf("%s://%s/",
1096 kExtensionScheme,
1097 embedder_extension_id().c_str()));
1098 return default_url.Resolve(src);
1101 void WebViewGuest::OnWebViewNewWindowResponse(
1102 int new_window_instance_id,
1103 bool allow,
1104 const std::string& user_input) {
1105 WebViewGuest* guest =
1106 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id);
1107 if (!guest)
1108 return;
1110 if (!allow)
1111 guest->Destroy();
1114 } // namespace extensions