Home | History | Annotate | Download | only in web_view
      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
   1159