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