Home | History | Annotate | Download | only in webview
      1 // Copyright 2013 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 "chrome/browser/guestview/webview/webview_guest.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
     10 #include "chrome/browser/extensions/extension_renderer_state.h"
     11 #include "chrome/browser/extensions/extension_web_contents_observer.h"
     12 #include "chrome/browser/extensions/script_executor.h"
     13 #include "chrome/browser/favicon/favicon_tab_helper.h"
     14 #include "chrome/browser/guestview/guestview_constants.h"
     15 #include "chrome/browser/guestview/webview/webview_constants.h"
     16 #include "chrome/browser/guestview/webview/webview_permission_types.h"
     17 #include "chrome/common/chrome_version_info.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "content/public/browser/native_web_keyboard_event.h"
     20 #include "content/public/browser/navigation_entry.h"
     21 #include "content/public/browser/notification_details.h"
     22 #include "content/public/browser/notification_source.h"
     23 #include "content/public/browser/notification_types.h"
     24 #include "content/public/browser/render_process_host.h"
     25 #include "content/public/browser/resource_request_details.h"
     26 #include "content/public/browser/site_instance.h"
     27 #include "content/public/browser/storage_partition.h"
     28 #include "content/public/browser/user_metrics.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "content/public/common/content_switches.h"
     31 #include "content/public/common/result_codes.h"
     32 #include "extensions/common/constants.h"
     33 #include "net/base/net_errors.h"
     34 
     35 #if defined(ENABLE_PLUGINS)
     36 #include "chrome/browser/guestview/webview/plugin_permission_helper.h"
     37 #endif
     38 
     39 using content::WebContents;
     40 using content::UserMetricsAction;
     41 
     42 namespace {
     43 
     44 static std::string TerminationStatusToString(base::TerminationStatus status) {
     45   switch (status) {
     46     case base::TERMINATION_STATUS_NORMAL_TERMINATION:
     47       return "normal";
     48     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
     49     case base::TERMINATION_STATUS_STILL_RUNNING:
     50       return "abnormal";
     51     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
     52       return "killed";
     53     case base::TERMINATION_STATUS_PROCESS_CRASHED:
     54 #if defined(OS_ANDROID)
     55     case base::TERMINATION_STATUS_OOM_PROTECTED:
     56 #endif
     57       return "crashed";
     58     case base::TERMINATION_STATUS_MAX_ENUM:
     59       break;
     60   }
     61   NOTREACHED() << "Unknown Termination Status.";
     62   return "unknown";
     63 }
     64 
     65 static std::string PermissionTypeToString(BrowserPluginPermissionType type) {
     66   switch (type) {
     67     case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD:
     68       return webview::kPermissionTypeDownload;
     69     case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION:
     70       return webview::kPermissionTypeGeolocation;
     71     case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA:
     72       return webview::kPermissionTypeMedia;
     73     case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW:
     74       return webview::kPermissionTypeNewWindow;
     75     case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK:
     76       return webview::kPermissionTypePointerLock;
     77     case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
     78       return webview::kPermissionTypeDialog;
     79     case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN:
     80       NOTREACHED();
     81       break;
     82     default: {
     83       WebViewPermissionType webview = static_cast<WebViewPermissionType>(type);
     84       switch (webview) {
     85         case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN:
     86           return webview::kPermissionTypeLoadPlugin;
     87       }
     88       NOTREACHED();
     89     }
     90   }
     91   return std::string();
     92 }
     93 
     94 void RemoveWebViewEventListenersOnIOThread(
     95     void* profile,
     96     const std::string& extension_id,
     97     int embedder_process_id,
     98     int view_instance_id) {
     99   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    100   ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
    101       profile,
    102       extension_id,
    103       embedder_process_id,
    104       view_instance_id);
    105 }
    106 
    107 void AttachWebViewHelpers(WebContents* contents) {
    108   FaviconTabHelper::CreateForWebContents(contents);
    109   extensions::ExtensionWebContentsObserver::CreateForWebContents(contents);
    110 #if defined(ENABLE_PLUGINS)
    111   PluginPermissionHelper::CreateForWebContents(contents);
    112 #endif
    113 }
    114 
    115 }  // namespace
    116 
    117 WebViewGuest::WebViewGuest(WebContents* guest_web_contents,
    118                            const std::string& extension_id)
    119     : GuestView(guest_web_contents, extension_id),
    120       WebContentsObserver(guest_web_contents),
    121       script_executor_(new extensions::ScriptExecutor(guest_web_contents,
    122                                                       &script_observers_)),
    123       next_permission_request_id_(0),
    124       is_overriding_user_agent_(false),
    125       pending_reload_on_attachment_(false) {
    126   notification_registrar_.Add(
    127       this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
    128       content::Source<WebContents>(guest_web_contents));
    129 
    130   notification_registrar_.Add(
    131       this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
    132       content::Source<WebContents>(guest_web_contents));
    133 
    134   AttachWebViewHelpers(guest_web_contents);
    135 }
    136 
    137 // static
    138 WebViewGuest* WebViewGuest::From(int embedder_process_id,
    139                                  int guest_instance_id) {
    140   GuestView* guest = GuestView::From(embedder_process_id, guest_instance_id);
    141   if (!guest)
    142     return NULL;
    143   return guest->AsWebView();
    144 }
    145 
    146 // static
    147 WebViewGuest* WebViewGuest::FromWebContents(WebContents* contents) {
    148   GuestView* guest = GuestView::FromWebContents(contents);
    149   return guest ? guest->AsWebView() : NULL;
    150 }
    151 
    152 // static
    153 void WebViewGuest::RecordUserInitiatedUMA(const PermissionResponseInfo& info,
    154                                           bool allow) {
    155   if (allow) {
    156     // Note that |allow| == true means the embedder explicitly allowed the
    157     // request. For some requests they might still fail. An example of such
    158     // scenario would be: an embedder allows geolocation request but doesn't
    159     // have geolocation access on its own.
    160     switch (info.permission_type) {
    161       case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD:
    162         RecordAction(
    163             UserMetricsAction("BrowserPlugin.PermissionAllow.Download"));
    164         break;
    165       case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION:
    166         RecordAction(
    167             UserMetricsAction("BrowserPlugin.PermissionAllow.Geolocation"));
    168         break;
    169       case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA:
    170         RecordAction(
    171             UserMetricsAction("BrowserPlugin.PermissionAllow.Media"));
    172         break;
    173       case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK:
    174         RecordAction(
    175             UserMetricsAction("BrowserPlugin.PermissionAllow.PointerLock"));
    176         break;
    177       case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW:
    178         RecordAction(
    179             UserMetricsAction("BrowserPlugin.PermissionAllow.NewWindow"));
    180         break;
    181       case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
    182         RecordAction(
    183             UserMetricsAction("BrowserPlugin.PermissionAllow.JSDialog"));
    184         break;
    185       case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN:
    186         break;
    187       default: {
    188         WebViewPermissionType webview_permission_type =
    189             static_cast<WebViewPermissionType>(info.permission_type);
    190         switch (webview_permission_type) {
    191           case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN:
    192             RecordAction(
    193                 UserMetricsAction("WebView.Guest.PermissionAllow.PluginLoad"));
    194             break;
    195           default:
    196             break;
    197         }
    198       }
    199     }
    200   } else {
    201     switch (info.permission_type) {
    202       case BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD:
    203         RecordAction(
    204             UserMetricsAction("BrowserPlugin.PermissionDeny.Download"));
    205         break;
    206       case BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION:
    207         RecordAction(
    208             UserMetricsAction("BrowserPlugin.PermissionDeny.Geolocation"));
    209         break;
    210       case BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA:
    211         RecordAction(
    212             UserMetricsAction("BrowserPlugin.PermissionDeny.Media"));
    213         break;
    214       case BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK:
    215         RecordAction(
    216             UserMetricsAction("BrowserPlugin.PermissionDeny.PointerLock"));
    217         break;
    218       case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW:
    219         RecordAction(
    220             UserMetricsAction("BrowserPlugin.PermissionDeny.NewWindow"));
    221         break;
    222       case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG:
    223         RecordAction(
    224             UserMetricsAction("BrowserPlugin.PermissionDeny.JSDialog"));
    225         break;
    226       case BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN:
    227         break;
    228       default: {
    229         WebViewPermissionType webview_permission_type =
    230             static_cast<WebViewPermissionType>(info.permission_type);
    231         switch (webview_permission_type) {
    232           case WEB_VIEW_PERMISSION_TYPE_LOAD_PLUGIN:
    233             RecordAction(
    234                 UserMetricsAction("WebView.Guest.PermissionDeny.PluginLoad"));
    235             break;
    236           default:
    237             break;
    238         }
    239       }
    240     }
    241   }
    242 }
    243 
    244 void WebViewGuest::Attach(WebContents* embedder_web_contents,
    245                           const base::DictionaryValue& args) {
    246   std::string user_agent_override;
    247   if (args.GetString(webview::kParameterUserAgentOverride,
    248                      &user_agent_override)) {
    249     SetUserAgentOverride(user_agent_override);
    250   } else {
    251     SetUserAgentOverride("");
    252   }
    253 
    254   GuestView::Attach(embedder_web_contents, args);
    255 
    256   AddWebViewToExtensionRendererState();
    257 }
    258 
    259 GuestView::Type WebViewGuest::GetViewType() const {
    260   return GuestView::WEBVIEW;
    261 }
    262 
    263 WebViewGuest* WebViewGuest::AsWebView() {
    264   return this;
    265 }
    266 
    267 AdViewGuest* WebViewGuest::AsAdView() {
    268   return NULL;
    269 }
    270 
    271 void WebViewGuest::AddMessageToConsole(int32 level,
    272                                        const base::string16& message,
    273                                        int32 line_no,
    274                                        const base::string16& source_id) {
    275   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    276   // Log levels are from base/logging.h: LogSeverity.
    277   args->SetInteger(webview::kLevel, level);
    278   args->SetString(webview::kMessage, message);
    279   args->SetInteger(webview::kLine, line_no);
    280   args->SetString(webview::kSourceId, source_id);
    281   DispatchEvent(
    282       new GuestView::Event(webview::kEventConsoleMessage, args.Pass()));
    283 }
    284 
    285 void WebViewGuest::Close() {
    286   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    287   DispatchEvent(new GuestView::Event(webview::kEventClose, args.Pass()));
    288 }
    289 
    290 void WebViewGuest::DidAttach() {
    291   if (pending_reload_on_attachment_) {
    292     pending_reload_on_attachment_ = false;
    293     guest_web_contents()->GetController().Reload(false);
    294   }
    295 }
    296 
    297 void WebViewGuest::EmbedderDestroyed() {
    298   // TODO(fsamuel): WebRequest event listeners for <webview> should survive
    299   // reparenting of a <webview> within a single embedder. Right now, we keep
    300   // around the browser state for the listener for the lifetime of the embedder.
    301   // Ideally, the lifetime of the listeners should match the lifetime of the
    302   // <webview> DOM node. Once http://crbug.com/156219 is resolved we can move
    303   // the call to RemoveWebViewEventListenersOnIOThread back to
    304   // WebViewGuest::WebContentsDestroyed.
    305   content::BrowserThread::PostTask(
    306       content::BrowserThread::IO,
    307       FROM_HERE,
    308       base::Bind(
    309           &RemoveWebViewEventListenersOnIOThread,
    310           browser_context(), extension_id(),
    311           embedder_render_process_id(),
    312           view_instance_id()));
    313 }
    314 
    315 void WebViewGuest::GuestProcessGone(base::TerminationStatus status) {
    316   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    317   args->SetInteger(webview::kProcessId,
    318                    web_contents()->GetRenderProcessHost()->GetID());
    319   args->SetString(webview::kReason, TerminationStatusToString(status));
    320   DispatchEvent(
    321       new GuestView::Event(webview::kEventExit, args.Pass()));
    322 }
    323 
    324 bool WebViewGuest::HandleKeyboardEvent(
    325     const content::NativeWebKeyboardEvent& event) {
    326   if (event.type != blink::WebInputEvent::RawKeyDown)
    327     return false;
    328 
    329 #if defined(OS_MACOSX)
    330   if (event.modifiers != blink::WebInputEvent::MetaKey)
    331     return false;
    332 
    333   if (event.windowsKeyCode == ui::VKEY_OEM_4) {
    334     Go(-1);
    335     return true;
    336   }
    337 
    338   if (event.windowsKeyCode == ui::VKEY_OEM_6) {
    339     Go(1);
    340     return true;
    341   }
    342 #else
    343   if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) {
    344     Go(-1);
    345     return true;
    346   }
    347 
    348   if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) {
    349     Go(1);
    350     return true;
    351   }
    352 #endif
    353   return false;
    354 }
    355 
    356 bool WebViewGuest::IsDragAndDropEnabled() {
    357 #if defined(OS_CHROMEOS)
    358   return true;
    359 #else
    360   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    361   if (channel != chrome::VersionInfo::CHANNEL_STABLE &&
    362       channel != chrome::VersionInfo::CHANNEL_BETA) {
    363     // Drag and drop is enabled in canary and dev channel.
    364     return true;
    365   }
    366 
    367   return CommandLine::ForCurrentProcess()->HasSwitch(
    368       switches::kEnableBrowserPluginDragDrop);
    369 #endif
    370 }
    371 
    372 bool WebViewGuest::IsOverridingUserAgent() const {
    373   return is_overriding_user_agent_;
    374 }
    375 
    376 void WebViewGuest::LoadProgressed(double progress) {
    377   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    378   args->SetString(guestview::kUrl, web_contents()->GetURL().spec());
    379   args->SetDouble(webview::kProgress, progress);
    380   DispatchEvent(new GuestView::Event(webview::kEventLoadProgress, args.Pass()));
    381 }
    382 
    383 void WebViewGuest::LoadAbort(bool is_top_level,
    384                              const GURL& url,
    385                              const std::string& error_type) {
    386   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    387   args->SetBoolean(guestview::kIsTopLevel, is_top_level);
    388   args->SetString(guestview::kUrl, url.possibly_invalid_spec());
    389   args->SetString(guestview::kReason, error_type);
    390   DispatchEvent(new GuestView::Event(webview::kEventLoadAbort, args.Pass()));
    391 }
    392 
    393 // TODO(fsamuel): Find a reliable way to test the 'responsive' and
    394 // 'unresponsive' events.
    395 void WebViewGuest::RendererResponsive() {
    396   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    397   args->SetInteger(webview::kProcessId,
    398       guest_web_contents()->GetRenderProcessHost()->GetID());
    399   DispatchEvent(new GuestView::Event(webview::kEventResponsive, args.Pass()));
    400 }
    401 
    402 void WebViewGuest::RendererUnresponsive() {
    403   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    404   args->SetInteger(webview::kProcessId,
    405       guest_web_contents()->GetRenderProcessHost()->GetID());
    406   DispatchEvent(new GuestView::Event(webview::kEventUnresponsive, args.Pass()));
    407 }
    408 
    409 bool WebViewGuest::RequestPermission(
    410     BrowserPluginPermissionType permission_type,
    411     const base::DictionaryValue& request_info,
    412     const PermissionResponseCallback& callback,
    413     bool allowed_by_default) {
    414   // If there are too many pending permission requests then reject this request.
    415   if (pending_permission_requests_.size() >=
    416       webview::kMaxOutstandingPermissionRequests) {
    417     callback.Run(false, std::string());
    418     return true;
    419   }
    420 
    421   int request_id = next_permission_request_id_++;
    422   pending_permission_requests_[request_id] =
    423       PermissionResponseInfo(callback, permission_type, allowed_by_default);
    424   scoped_ptr<base::DictionaryValue> args(request_info.DeepCopy());
    425   args->SetInteger(webview::kRequestId, request_id);
    426   switch (permission_type) {
    427     case BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW: {
    428       DispatchEvent(new GuestView::Event(webview::kEventNewWindow,
    429                                          args.Pass()));
    430       break;
    431     }
    432     case BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG: {
    433       chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    434       if (channel > chrome::VersionInfo::CHANNEL_DEV) {
    435         // 'dialog' API is not available in stable/beta.
    436         callback.Run(false, std::string());
    437         return true;
    438       }
    439       DispatchEvent(new GuestView::Event(webview::kEventDialog,
    440                                          args.Pass()));
    441       break;
    442     }
    443     default: {
    444       args->SetString(webview::kPermission,
    445                       PermissionTypeToString(permission_type));
    446       DispatchEvent(new GuestView::Event(webview::kEventPermissionRequest,
    447                                          args.Pass()));
    448       break;
    449     }
    450   }
    451   return true;
    452 }
    453 
    454 void WebViewGuest::Observe(int type,
    455                            const content::NotificationSource& source,
    456                            const content::NotificationDetails& details) {
    457   switch (type) {
    458     case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
    459       DCHECK_EQ(content::Source<WebContents>(source).ptr(),
    460                 guest_web_contents());
    461       if (content::Source<WebContents>(source).ptr() == guest_web_contents())
    462         LoadHandlerCalled();
    463       break;
    464     }
    465     case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
    466       DCHECK_EQ(content::Source<WebContents>(source).ptr(),
    467                 guest_web_contents());
    468       content::ResourceRedirectDetails* resource_redirect_details =
    469           content::Details<content::ResourceRedirectDetails>(details).ptr();
    470       bool is_top_level =
    471           resource_redirect_details->resource_type == ResourceType::MAIN_FRAME;
    472       LoadRedirect(resource_redirect_details->url,
    473                    resource_redirect_details->new_url,
    474                    is_top_level);
    475       break;
    476     }
    477     default:
    478       NOTREACHED() << "Unexpected notification sent.";
    479       break;
    480   }
    481 }
    482 
    483 void WebViewGuest::Go(int relative_index) {
    484   guest_web_contents()->GetController().GoToOffset(relative_index);
    485 }
    486 
    487 void WebViewGuest::Reload() {
    488   // TODO(fsamuel): Don't check for repost because we don't want to show
    489   // Chromium's repost warning. We might want to implement a separate API
    490   // for registering a callback if a repost is about to happen.
    491   guest_web_contents()->GetController().Reload(false);
    492 }
    493 
    494 WebViewGuest::SetPermissionResult WebViewGuest::SetPermission(
    495     int request_id,
    496     PermissionResponseAction action,
    497     const std::string& user_input) {
    498   RequestMap::iterator request_itr =
    499       pending_permission_requests_.find(request_id);
    500 
    501   if (request_itr == pending_permission_requests_.end())
    502     return SET_PERMISSION_INVALID;
    503 
    504   const PermissionResponseInfo& info = request_itr->second;
    505   bool allow = (action == ALLOW) ||
    506       ((action == DEFAULT) && info.allowed_by_default);
    507 
    508   info.callback.Run(allow, user_input);
    509 
    510   // Only record user initiated (i.e. non-default) actions.
    511   if (action != DEFAULT)
    512     RecordUserInitiatedUMA(info, allow);
    513 
    514   pending_permission_requests_.erase(request_itr);
    515 
    516   return allow ? SET_PERMISSION_ALLOWED : SET_PERMISSION_DENIED;
    517 }
    518 
    519 void WebViewGuest::SetUserAgentOverride(
    520     const std::string& user_agent_override) {
    521   is_overriding_user_agent_ = !user_agent_override.empty();
    522   if (is_overriding_user_agent_) {
    523     content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA"));
    524   }
    525   guest_web_contents()->SetUserAgentOverride(user_agent_override);
    526 }
    527 
    528 void WebViewGuest::Stop() {
    529   guest_web_contents()->Stop();
    530 }
    531 
    532 void WebViewGuest::Terminate() {
    533   content::RecordAction(UserMetricsAction("WebView.Guest.Terminate"));
    534   base::ProcessHandle process_handle =
    535       guest_web_contents()->GetRenderProcessHost()->GetHandle();
    536   if (process_handle)
    537     base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false);
    538 }
    539 
    540 bool WebViewGuest::ClearData(const base::Time remove_since,
    541                              uint32 removal_mask,
    542                              const base::Closure& callback) {
    543   content::RecordAction(UserMetricsAction("WebView.Guest.ClearData"));
    544   content::StoragePartition* partition =
    545       content::BrowserContext::GetStoragePartition(
    546           web_contents()->GetBrowserContext(),
    547           web_contents()->GetSiteInstance());
    548 
    549   if (!partition)
    550     return false;
    551 
    552   partition->ClearData(
    553       removal_mask,
    554       content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
    555       NULL,
    556       content::StoragePartition::OriginMatcherFunction(),
    557       remove_since,
    558       base::Time::Now(),
    559       callback);
    560   return true;
    561 }
    562 
    563 WebViewGuest::~WebViewGuest() {
    564 }
    565 
    566 void WebViewGuest::DidCommitProvisionalLoadForFrame(
    567     int64 frame_id,
    568     const base::string16& frame_unique_name,
    569     bool is_main_frame,
    570     const GURL& url,
    571     content::PageTransition transition_type,
    572     content::RenderViewHost* render_view_host) {
    573   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    574   args->SetString(guestview::kUrl, url.spec());
    575   args->SetBoolean(guestview::kIsTopLevel, is_main_frame);
    576   args->SetInteger(webview::kInternalCurrentEntryIndex,
    577       web_contents()->GetController().GetCurrentEntryIndex());
    578   args->SetInteger(webview::kInternalEntryCount,
    579       web_contents()->GetController().GetEntryCount());
    580   args->SetInteger(webview::kInternalProcessId,
    581       web_contents()->GetRenderProcessHost()->GetID());
    582   DispatchEvent(new GuestView::Event(webview::kEventLoadCommit, args.Pass()));
    583 }
    584 
    585 void WebViewGuest::DidFailProvisionalLoad(
    586     int64 frame_id,
    587     const base::string16& frame_unique_name,
    588     bool is_main_frame,
    589     const GURL& validated_url,
    590     int error_code,
    591     const base::string16& error_description,
    592     content::RenderViewHost* render_view_host) {
    593   // Translate the |error_code| into an error string.
    594   std::string error_type;
    595   base::RemoveChars(net::ErrorToString(error_code), "net::", &error_type);
    596   LoadAbort(is_main_frame, validated_url, error_type);
    597 }
    598 
    599 void WebViewGuest::DidStartProvisionalLoadForFrame(
    600     int64 frame_id,
    601     int64 parent_frame_id,
    602     bool is_main_frame,
    603     const GURL& validated_url,
    604     bool is_error_page,
    605     bool is_iframe_srcdoc,
    606     content::RenderViewHost* render_view_host) {
    607   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    608   args->SetString(guestview::kUrl, validated_url.spec());
    609   args->SetBoolean(guestview::kIsTopLevel, is_main_frame);
    610   DispatchEvent(new GuestView::Event(webview::kEventLoadStart, args.Pass()));
    611 }
    612 
    613 void WebViewGuest::DidStopLoading(content::RenderViewHost* render_view_host) {
    614   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    615   DispatchEvent(new GuestView::Event(webview::kEventLoadStop, args.Pass()));
    616 }
    617 
    618 void WebViewGuest::WebContentsDestroyed(WebContents* web_contents) {
    619   RemoveWebViewFromExtensionRendererState(web_contents);
    620 }
    621 
    622 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) {
    623   content::NavigationController& controller =
    624       guest_web_contents()->GetController();
    625   content::NavigationEntry* entry = controller.GetVisibleEntry();
    626   if (!entry)
    627     return;
    628   entry->SetIsOverridingUserAgent(!user_agent.empty());
    629   if (!attached()) {
    630     // We cannot reload now because all resource loads are suspended until
    631     // attachment.
    632     pending_reload_on_attachment_ = true;
    633     return;
    634   }
    635   guest_web_contents()->GetController().Reload(false);
    636 }
    637 
    638 void WebViewGuest::LoadHandlerCalled() {
    639   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    640   DispatchEvent(new GuestView::Event(webview::kEventContentLoad, args.Pass()));
    641 }
    642 
    643 void WebViewGuest::LoadRedirect(const GURL& old_url,
    644                                 const GURL& new_url,
    645                                 bool is_top_level) {
    646   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    647   args->SetBoolean(guestview::kIsTopLevel, is_top_level);
    648   args->SetString(webview::kNewURL, new_url.spec());
    649   args->SetString(webview::kOldURL, old_url.spec());
    650   DispatchEvent(new GuestView::Event(webview::kEventLoadRedirect, args.Pass()));
    651 }
    652 
    653 // static
    654 bool WebViewGuest::AllowChromeExtensionURLs() {
    655   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    656   return channel <= chrome::VersionInfo::CHANNEL_DEV;
    657 }
    658 
    659 void WebViewGuest::AddWebViewToExtensionRendererState() {
    660   const GURL& site_url = web_contents()->GetSiteInstance()->GetSiteURL();
    661   std::string partition_domain;
    662   std::string partition_id;
    663   bool in_memory;
    664   if (!GetGuestPartitionConfigForSite(
    665           site_url, &partition_domain, &partition_id, &in_memory)) {
    666     NOTREACHED();
    667     return;
    668   }
    669   DCHECK(extension_id() == partition_domain);
    670 
    671   ExtensionRendererState::WebViewInfo webview_info;
    672   webview_info.embedder_process_id = embedder_render_process_id();
    673   webview_info.instance_id = view_instance_id();
    674   webview_info.partition_id =  partition_id;
    675   webview_info.extension_id = extension_id();
    676   webview_info.allow_chrome_extension_urls = AllowChromeExtensionURLs();
    677 
    678   content::BrowserThread::PostTask(
    679       content::BrowserThread::IO, FROM_HERE,
    680       base::Bind(
    681           &ExtensionRendererState::AddWebView,
    682           base::Unretained(ExtensionRendererState::GetInstance()),
    683           guest_web_contents()->GetRenderProcessHost()->GetID(),
    684           guest_web_contents()->GetRoutingID(),
    685           webview_info));
    686 }
    687 
    688 // static
    689 void WebViewGuest::RemoveWebViewFromExtensionRendererState(
    690     WebContents* web_contents) {
    691   content::BrowserThread::PostTask(
    692       content::BrowserThread::IO, FROM_HERE,
    693       base::Bind(
    694           &ExtensionRendererState::RemoveWebView,
    695           base::Unretained(ExtensionRendererState::GetInstance()),
    696           web_contents->GetRenderProcessHost()->GetID(),
    697           web_contents->GetRoutingID()));
    698 }
    699 
    700 GURL WebViewGuest::ResolveURL(const std::string& src) {
    701   if (extension_id().empty()) {
    702     NOTREACHED();
    703     return GURL(src);
    704   }
    705 
    706   // Only resolve URL to chrome-extension:// if we support such URLs.
    707   if (!AllowChromeExtensionURLs())
    708     return GURL(src);
    709 
    710   GURL default_url(base::StringPrintf("%s://%s/",
    711                                       extensions::kExtensionScheme,
    712                                       extension_id().c_str()));
    713   return default_url.Resolve(src);
    714 }
    715 
    716 void WebViewGuest::SizeChanged(const gfx::Size& old_size,
    717                                const gfx::Size& new_size) {
    718   scoped_ptr<DictionaryValue> args(new DictionaryValue());
    719   args->SetInteger(webview::kOldHeight, old_size.height());
    720   args->SetInteger(webview::kOldWidth, old_size.width());
    721   args->SetInteger(webview::kNewHeight, new_size.height());
    722   args->SetInteger(webview::kNewWidth, new_size.width());
    723   DispatchEvent(new GuestView::Event(webview::kEventSizeChanged, args.Pass()));
    724 }
    725 
    726 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo()
    727     : permission_type(BROWSER_PLUGIN_PERMISSION_TYPE_UNKNOWN),
    728       allowed_by_default(false) {
    729 }
    730 
    731 WebViewGuest::PermissionResponseInfo::PermissionResponseInfo(
    732     const PermissionResponseCallback& callback,
    733     BrowserPluginPermissionType permission_type,
    734     bool allowed_by_default)
    735     : callback(callback),
    736       permission_type(permission_type),
    737       allowed_by_default(allowed_by_default) {
    738 }
    739 
    740 WebViewGuest::PermissionResponseInfo::~PermissionResponseInfo() {
    741 }
    742