Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2012 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 "android_webview/browser/aw_content_browser_client.h"
      6 
      7 #include "android_webview/browser/aw_browser_context.h"
      8 #include "android_webview/browser/aw_browser_main_parts.h"
      9 #include "android_webview/browser/aw_browser_permission_request_delegate.h"
     10 #include "android_webview/browser/aw_contents_client_bridge_base.h"
     11 #include "android_webview/browser/aw_contents_io_thread_client.h"
     12 #include "android_webview/browser/aw_cookie_access_policy.h"
     13 #include "android_webview/browser/aw_quota_permission_context.h"
     14 #include "android_webview/browser/aw_web_preferences_populater.h"
     15 #include "android_webview/browser/jni_dependency_factory.h"
     16 #include "android_webview/browser/net_disk_cache_remover.h"
     17 #include "android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h"
     18 #include "android_webview/common/render_view_messages.h"
     19 #include "android_webview/common/url_constants.h"
     20 #include "base/base_paths_android.h"
     21 #include "base/path_service.h"
     22 #include "components/cdm/browser/cdm_message_filter_android.h"
     23 #include "content/public/browser/access_token_store.h"
     24 #include "content/public/browser/browser_message_filter.h"
     25 #include "content/public/browser/browser_thread.h"
     26 #include "content/public/browser/child_process_security_policy.h"
     27 #include "content/public/browser/render_process_host.h"
     28 #include "content/public/browser/render_view_host.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "content/public/common/url_constants.h"
     31 #include "grit/ui_resources.h"
     32 #include "net/android/network_library.h"
     33 #include "net/ssl/ssl_cert_request_info.h"
     34 #include "net/ssl/ssl_info.h"
     35 #include "ui/base/l10n/l10n_util_android.h"
     36 #include "ui/base/resource/resource_bundle.h"
     37 #include "webkit/common/webpreferences.h"
     38 
     39 using content::BrowserThread;
     40 
     41 namespace android_webview {
     42 namespace {
     43 
     44 // TODO(sgurun) move this to its own file.
     45 // This class filters out incoming aw_contents related IPC messages for the
     46 // renderer process on the IPC thread.
     47 class AwContentsMessageFilter : public content::BrowserMessageFilter {
     48 public:
     49   explicit AwContentsMessageFilter(int process_id);
     50 
     51   // BrowserMessageFilter methods.
     52   virtual void OverrideThreadForMessage(
     53       const IPC::Message& message,
     54       BrowserThread::ID* thread) OVERRIDE;
     55   virtual bool OnMessageReceived(
     56       const IPC::Message& message) OVERRIDE;
     57 
     58   void OnShouldOverrideUrlLoading(int routing_id,
     59                                   const base::string16& url,
     60                                   bool* ignore_navigation);
     61   void OnSubFrameCreated(int parent_render_frame_id, int child_render_frame_id);
     62 
     63 private:
     64   virtual ~AwContentsMessageFilter();
     65 
     66   int process_id_;
     67 
     68   DISALLOW_COPY_AND_ASSIGN(AwContentsMessageFilter);
     69 };
     70 
     71 AwContentsMessageFilter::AwContentsMessageFilter(int process_id)
     72     : BrowserMessageFilter(AndroidWebViewMsgStart),
     73       process_id_(process_id) {
     74 }
     75 
     76 AwContentsMessageFilter::~AwContentsMessageFilter() {
     77 }
     78 
     79 void AwContentsMessageFilter::OverrideThreadForMessage(
     80     const IPC::Message& message, BrowserThread::ID* thread) {
     81   if (message.type() == AwViewHostMsg_ShouldOverrideUrlLoading::ID) {
     82     *thread = BrowserThread::UI;
     83   }
     84 }
     85 
     86 bool AwContentsMessageFilter::OnMessageReceived(const IPC::Message& message) {
     87   bool handled = true;
     88   IPC_BEGIN_MESSAGE_MAP(AwContentsMessageFilter, message)
     89     IPC_MESSAGE_HANDLER(AwViewHostMsg_ShouldOverrideUrlLoading,
     90                         OnShouldOverrideUrlLoading)
     91     IPC_MESSAGE_HANDLER(AwViewHostMsg_SubFrameCreated, OnSubFrameCreated)
     92     IPC_MESSAGE_UNHANDLED(handled = false)
     93   IPC_END_MESSAGE_MAP()
     94   return handled;
     95 }
     96 
     97 void AwContentsMessageFilter::OnShouldOverrideUrlLoading(
     98     int render_frame_id,
     99     const base::string16& url,
    100     bool* ignore_navigation) {
    101   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    102   *ignore_navigation = false;
    103   AwContentsClientBridgeBase* client =
    104       AwContentsClientBridgeBase::FromID(process_id_, render_frame_id);
    105   if (client) {
    106     *ignore_navigation = client->ShouldOverrideUrlLoading(url);
    107   } else {
    108     LOG(WARNING) << "Failed to find the associated render view host for url: "
    109                  << url;
    110   }
    111 }
    112 
    113 void AwContentsMessageFilter::OnSubFrameCreated(int parent_render_frame_id,
    114                                                 int child_render_frame_id) {
    115   AwContentsIoThreadClient::SubFrameCreated(
    116       process_id_, parent_render_frame_id, child_render_frame_id);
    117 }
    118 
    119 class AwAccessTokenStore : public content::AccessTokenStore {
    120  public:
    121   AwAccessTokenStore() { }
    122 
    123   // content::AccessTokenStore implementation
    124   virtual void LoadAccessTokens(
    125       const LoadAccessTokensCallbackType& request) OVERRIDE {
    126     AccessTokenStore::AccessTokenSet access_token_set;
    127     // AccessTokenSet and net::URLRequestContextGetter not used on Android,
    128     // but Run needs to be called to finish the geolocation setup.
    129     request.Run(access_token_set, NULL);
    130   }
    131   virtual void SaveAccessToken(const GURL& server_url,
    132                                const base::string16& access_token) OVERRIDE { }
    133 
    134  private:
    135   virtual ~AwAccessTokenStore() { }
    136 
    137   DISALLOW_COPY_AND_ASSIGN(AwAccessTokenStore);
    138 };
    139 
    140 void CancelProtectedMediaIdentifierPermissionRequests(
    141     int render_process_id,
    142     int render_view_id,
    143     const GURL& origin) {
    144   AwBrowserPermissionRequestDelegate* delegate =
    145       AwBrowserPermissionRequestDelegate::FromID(render_process_id,
    146                                                  render_view_id);
    147   if (delegate)
    148     delegate->CancelProtectedMediaIdentifierPermissionRequests(origin);
    149 }
    150 
    151 void CancelGeolocationPermissionRequests(
    152     int render_process_id,
    153     int render_view_id,
    154     const GURL& origin) {
    155   AwBrowserPermissionRequestDelegate* delegate =
    156       AwBrowserPermissionRequestDelegate::FromID(render_process_id,
    157                                                  render_view_id);
    158   if (delegate)
    159     delegate->CancelGeolocationPermissionRequests(origin);
    160 }
    161 
    162 }  // namespace
    163 
    164 std::string AwContentBrowserClient::GetAcceptLangsImpl() {
    165   // Start with the currnet locale.
    166   std::string langs = l10n_util::GetDefaultLocale();
    167 
    168   // If we're not en-US, add in en-US which will be
    169   // used with a lower q-value.
    170   if (StringToLowerASCII(langs) != "en-us") {
    171     langs += ",en-US";
    172   }
    173   return langs;
    174 }
    175 
    176 AwBrowserContext* AwContentBrowserClient::GetAwBrowserContext() {
    177   return AwBrowserContext::GetDefault();
    178 }
    179 
    180 AwContentBrowserClient::AwContentBrowserClient(
    181     JniDependencyFactory* native_factory)
    182     : native_factory_(native_factory) {
    183   base::FilePath user_data_dir;
    184   if (!PathService::Get(base::DIR_ANDROID_APP_DATA, &user_data_dir)) {
    185     NOTREACHED() << "Failed to get app data directory for Android WebView";
    186   }
    187   browser_context_.reset(
    188       new AwBrowserContext(user_data_dir, native_factory_));
    189 }
    190 
    191 AwContentBrowserClient::~AwContentBrowserClient() {
    192 }
    193 
    194 void AwContentBrowserClient::AddCertificate(net::CertificateMimeType cert_type,
    195                                             const void* cert_data,
    196                                             size_t cert_size,
    197                                             int render_process_id,
    198                                             int render_frame_id) {
    199   if (cert_size > 0)
    200     net::android::StoreCertificate(cert_type, cert_data, cert_size);
    201 }
    202 
    203 content::BrowserMainParts* AwContentBrowserClient::CreateBrowserMainParts(
    204     const content::MainFunctionParams& parameters) {
    205   return new AwBrowserMainParts(browser_context_.get());
    206 }
    207 
    208 content::WebContentsViewDelegate*
    209 AwContentBrowserClient::GetWebContentsViewDelegate(
    210     content::WebContents* web_contents) {
    211   return native_factory_->CreateViewDelegate(web_contents);
    212 }
    213 
    214 void AwContentBrowserClient::RenderProcessWillLaunch(
    215     content::RenderProcessHost* host) {
    216   // If WebView becomes multi-process capable, this may be insecure.
    217   // More benefit can be derived from the ChildProcessSecurotyPolicy by
    218   // deferring the GrantScheme calls until we know that a given child process
    219   // really does need that priviledge. Check here to ensure we rethink this
    220   // when the time comes. See crbug.com/156062.
    221   CHECK(content::RenderProcessHost::run_renderer_in_process());
    222 
    223   // Grant content: and file: scheme to the whole process, since we impose
    224   // per-view access checks.
    225   content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
    226       host->GetID(), android_webview::kContentScheme);
    227   content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
    228       host->GetID(), url::kFileScheme);
    229 
    230   host->AddFilter(new AwContentsMessageFilter(host->GetID()));
    231   host->AddFilter(new cdm::CdmMessageFilterAndroid());
    232 }
    233 
    234 net::URLRequestContextGetter* AwContentBrowserClient::CreateRequestContext(
    235     content::BrowserContext* browser_context,
    236     content::ProtocolHandlerMap* protocol_handlers,
    237     content::URLRequestInterceptorScopedVector request_interceptors) {
    238   DCHECK(browser_context_.get() == browser_context);
    239   return browser_context_->CreateRequestContext(protocol_handlers,
    240                                                 request_interceptors.Pass());
    241 }
    242 
    243 net::URLRequestContextGetter*
    244 AwContentBrowserClient::CreateRequestContextForStoragePartition(
    245     content::BrowserContext* browser_context,
    246     const base::FilePath& partition_path,
    247     bool in_memory,
    248     content::ProtocolHandlerMap* protocol_handlers,
    249     content::URLRequestInterceptorScopedVector request_interceptors) {
    250   DCHECK(browser_context_.get() == browser_context);
    251   // TODO(mkosiba,kinuko): request_interceptors should be hooked up in the
    252   // downstream. (crbug.com/350286)
    253   return browser_context_->CreateRequestContextForStoragePartition(
    254       partition_path, in_memory, protocol_handlers,
    255       request_interceptors.Pass());
    256 }
    257 
    258 std::string AwContentBrowserClient::GetCanonicalEncodingNameByAliasName(
    259     const std::string& alias_name) {
    260   return alias_name;
    261 }
    262 
    263 void AwContentBrowserClient::AppendExtraCommandLineSwitches(
    264     base::CommandLine* command_line,
    265     int child_process_id) {
    266   NOTREACHED() << "Android WebView does not support multi-process yet";
    267 }
    268 
    269 std::string AwContentBrowserClient::GetApplicationLocale() {
    270   return l10n_util::GetDefaultLocale();
    271 }
    272 
    273 std::string AwContentBrowserClient::GetAcceptLangs(
    274     content::BrowserContext* context) {
    275   return GetAcceptLangsImpl();
    276 }
    277 
    278 const gfx::ImageSkia* AwContentBrowserClient::GetDefaultFavicon() {
    279   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    280   // TODO(boliu): Bundle our own default favicon?
    281   return rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
    282 }
    283 
    284 bool AwContentBrowserClient::AllowAppCache(const GURL& manifest_url,
    285                            const GURL& first_party,
    286                            content::ResourceContext* context) {
    287   // WebView doesn't have a per-site policy for locally stored data,
    288   // instead AppCache can be disabled for individual WebViews.
    289   return true;
    290 }
    291 
    292 
    293 bool AwContentBrowserClient::AllowGetCookie(const GURL& url,
    294                                             const GURL& first_party,
    295                                             const net::CookieList& cookie_list,
    296                                             content::ResourceContext* context,
    297                                             int render_process_id,
    298                                             int render_frame_id) {
    299   return AwCookieAccessPolicy::GetInstance()->AllowGetCookie(url,
    300                                                              first_party,
    301                                                              cookie_list,
    302                                                              context,
    303                                                              render_process_id,
    304                                                              render_frame_id);
    305 }
    306 
    307 bool AwContentBrowserClient::AllowSetCookie(const GURL& url,
    308                                             const GURL& first_party,
    309                                             const std::string& cookie_line,
    310                                             content::ResourceContext* context,
    311                                             int render_process_id,
    312                                             int render_frame_id,
    313                                             net::CookieOptions* options) {
    314   return AwCookieAccessPolicy::GetInstance()->AllowSetCookie(url,
    315                                                              first_party,
    316                                                              cookie_line,
    317                                                              context,
    318                                                              render_process_id,
    319                                                              render_frame_id,
    320                                                              options);
    321 }
    322 
    323 bool AwContentBrowserClient::AllowWorkerDatabase(
    324     const GURL& url,
    325     const base::string16& name,
    326     const base::string16& display_name,
    327     unsigned long estimated_size,
    328     content::ResourceContext* context,
    329     const std::vector<std::pair<int, int> >& render_frames) {
    330   // Android WebView does not yet support web workers.
    331   return false;
    332 }
    333 
    334 bool AwContentBrowserClient::AllowWorkerFileSystem(
    335     const GURL& url,
    336     content::ResourceContext* context,
    337     const std::vector<std::pair<int, int> >& render_frames) {
    338   // Android WebView does not yet support web workers.
    339   return false;
    340 }
    341 
    342 bool AwContentBrowserClient::AllowWorkerIndexedDB(
    343     const GURL& url,
    344     const base::string16& name,
    345     content::ResourceContext* context,
    346     const std::vector<std::pair<int, int> >& render_frames) {
    347   // Android WebView does not yet support web workers.
    348   return false;
    349 }
    350 
    351 content::QuotaPermissionContext*
    352 AwContentBrowserClient::CreateQuotaPermissionContext() {
    353   return new AwQuotaPermissionContext;
    354 }
    355 
    356 void AwContentBrowserClient::AllowCertificateError(
    357     int render_process_id,
    358     int render_frame_id,
    359     int cert_error,
    360     const net::SSLInfo& ssl_info,
    361     const GURL& request_url,
    362     ResourceType::Type resource_type,
    363     bool overridable,
    364     bool strict_enforcement,
    365     const base::Callback<void(bool)>& callback,
    366     content::CertificateRequestResultType* result) {
    367   AwContentsClientBridgeBase* client =
    368       AwContentsClientBridgeBase::FromID(render_process_id, render_frame_id);
    369   bool cancel_request = true;
    370   if (client)
    371     client->AllowCertificateError(cert_error,
    372                                   ssl_info.cert.get(),
    373                                   request_url,
    374                                   callback,
    375                                   &cancel_request);
    376   if (cancel_request)
    377     *result = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY;
    378 }
    379 
    380 void AwContentBrowserClient::SelectClientCertificate(
    381       int render_process_id,
    382       int render_frame_id,
    383       const net::HttpNetworkSession* network_session,
    384       net::SSLCertRequestInfo* cert_request_info,
    385       const base::Callback<void(net::X509Certificate*)>& callback) {
    386   AwContentsClientBridgeBase* client =
    387       AwContentsClientBridgeBase::FromID(render_process_id, render_frame_id);
    388   if (client) {
    389     client->SelectClientCertificate(cert_request_info, callback);
    390   } else {
    391     callback.Run(NULL);
    392   }
    393 }
    394 
    395 blink::WebNotificationPresenter::Permission
    396     AwContentBrowserClient::CheckDesktopNotificationPermission(
    397         const GURL& source_url,
    398         content::ResourceContext* context,
    399         int render_process_id) {
    400   // Android WebView does not support notifications, so return Denied here.
    401   return blink::WebNotificationPresenter::PermissionDenied;
    402 }
    403 
    404 void AwContentBrowserClient::ShowDesktopNotification(
    405     const content::ShowDesktopNotificationHostMsgParams& params,
    406     content::RenderFrameHost* render_frame_host,
    407     content::DesktopNotificationDelegate* delegate,
    408     base::Closure* cancel_callback) {
    409   NOTREACHED() << "Android WebView does not support desktop notifications.";
    410 }
    411 
    412 void AwContentBrowserClient::RequestGeolocationPermission(
    413     content::WebContents* web_contents,
    414     int bridge_id,
    415     const GURL& requesting_frame,
    416     bool user_gesture,
    417     base::Callback<void(bool)> result_callback,
    418     base::Closure* cancel_callback) {
    419   int render_process_id = web_contents->GetRenderProcessHost()->GetID();
    420   int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
    421   AwBrowserPermissionRequestDelegate* delegate =
    422       AwBrowserPermissionRequestDelegate::FromID(render_process_id,
    423                                                  render_view_id);
    424   if (delegate == NULL) {
    425     DVLOG(0) << "Dropping GeolocationPermission request";
    426     result_callback.Run(false);
    427     return;
    428   }
    429 
    430   GURL origin = requesting_frame.GetOrigin();
    431   if (cancel_callback) {
    432     *cancel_callback = base::Bind(
    433         CancelGeolocationPermissionRequests, render_process_id, render_view_id,
    434         origin);
    435   }
    436   delegate->RequestGeolocationPermission(origin, result_callback);
    437 }
    438 
    439 void AwContentBrowserClient::RequestMidiSysExPermission(
    440     content::WebContents* web_contents,
    441     int bridge_id,
    442     const GURL& requesting_frame,
    443     bool user_gesture,
    444     base::Callback<void(bool)> result_callback,
    445     base::Closure* cancel_callback) {
    446   // TODO(toyoshim): Android WebView is not supported yet.
    447   // See http://crbug.com/339767.
    448   result_callback.Run(false);
    449 }
    450 
    451 void AwContentBrowserClient::RequestProtectedMediaIdentifierPermission(
    452     content::WebContents* web_contents,
    453     const GURL& origin,
    454     base::Callback<void(bool)> result_callback,
    455     base::Closure* cancel_callback) {
    456   int render_process_id = web_contents->GetRenderProcessHost()->GetID();
    457   int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
    458   AwBrowserPermissionRequestDelegate* delegate =
    459       AwBrowserPermissionRequestDelegate::FromID(render_process_id,
    460                                                  render_view_id);
    461   if (delegate == NULL) {
    462     DVLOG(0) << "Dropping ProtectedMediaIdentifierPermission request";
    463     result_callback.Run(false);
    464     return;
    465   }
    466 
    467   if (cancel_callback) {
    468     *cancel_callback = base::Bind(
    469         CancelProtectedMediaIdentifierPermissionRequests,
    470         render_process_id, render_view_id, origin);
    471   }
    472   delegate->RequestProtectedMediaIdentifierPermission(origin, result_callback);
    473 }
    474 
    475 bool AwContentBrowserClient::CanCreateWindow(
    476     const GURL& opener_url,
    477     const GURL& opener_top_level_frame_url,
    478     const GURL& source_origin,
    479     WindowContainerType container_type,
    480     const GURL& target_url,
    481     const content::Referrer& referrer,
    482     WindowOpenDisposition disposition,
    483     const blink::WebWindowFeatures& features,
    484     bool user_gesture,
    485     bool opener_suppressed,
    486     content::ResourceContext* context,
    487     int render_process_id,
    488     int opener_id,
    489     bool* no_javascript_access) {
    490   // We unconditionally allow popup windows at this stage and will give
    491   // the embedder the opporunity to handle displaying of the popup in
    492   // WebContentsDelegate::AddContents (via the
    493   // AwContentsClient.onCreateWindow callback).
    494   // Note that if the embedder has blocked support for creating popup
    495   // windows through AwSettings, then we won't get to this point as
    496   // the popup creation will have been blocked at the WebKit level.
    497   if (no_javascript_access) {
    498     *no_javascript_access = false;
    499   }
    500   return true;
    501 }
    502 
    503 std::string AwContentBrowserClient::GetWorkerProcessTitle(const GURL& url,
    504                                           content::ResourceContext* context) {
    505   NOTREACHED() << "Android WebView does not yet support web workers.";
    506   return std::string();
    507 }
    508 
    509 
    510 void AwContentBrowserClient::ResourceDispatcherHostCreated() {
    511   AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated();
    512 }
    513 
    514 net::NetLog* AwContentBrowserClient::GetNetLog() {
    515   // TODO(boliu): Implement AwNetLog.
    516   return NULL;
    517 }
    518 
    519 content::AccessTokenStore* AwContentBrowserClient::CreateAccessTokenStore() {
    520   return new AwAccessTokenStore();
    521 }
    522 
    523 bool AwContentBrowserClient::IsFastShutdownPossible() {
    524   NOTREACHED() << "Android WebView is single process, so IsFastShutdownPossible"
    525                << " should never be called";
    526   return false;
    527 }
    528 
    529 void AwContentBrowserClient::UpdateInspectorSetting(
    530     content::RenderViewHost* rvh,
    531     const std::string& key,
    532     const std::string& value) {
    533   // TODO(boliu): Implement persisting inspector settings.
    534   NOTIMPLEMENTED();
    535 }
    536 
    537 void AwContentBrowserClient::ClearCache(content::RenderViewHost* rvh) {
    538   RemoveHttpDiskCache(rvh->GetProcess()->GetBrowserContext(),
    539                       rvh->GetProcess()->GetID());
    540 }
    541 
    542 void AwContentBrowserClient::ClearCookies(content::RenderViewHost* rvh) {
    543   // TODO(boliu): Implement.
    544   NOTIMPLEMENTED();
    545 }
    546 
    547 base::FilePath AwContentBrowserClient::GetDefaultDownloadDirectory() {
    548   // Android WebView does not currently use the Chromium downloads system.
    549   // Download requests are cancelled immedately when recognized; see
    550   // AwResourceDispatcherHost::CreateResourceHandlerForDownload. However the
    551   // download system still tries to start up and calls this before recognizing
    552   // the request has been cancelled.
    553   return base::FilePath();
    554 }
    555 
    556 std::string AwContentBrowserClient::GetDefaultDownloadName() {
    557   NOTREACHED() << "Android WebView does not use chromium downloads";
    558   return std::string();
    559 }
    560 
    561 void AwContentBrowserClient::DidCreatePpapiPlugin(
    562     content::BrowserPpapiHost* browser_host) {
    563   NOTREACHED() << "Android WebView does not support plugins";
    564 }
    565 
    566 bool AwContentBrowserClient::AllowPepperSocketAPI(
    567     content::BrowserContext* browser_context,
    568     const GURL& url,
    569     bool private_api,
    570     const content::SocketPermissionRequest* params) {
    571   NOTREACHED() << "Android WebView does not support plugins";
    572   return false;
    573 }
    574 
    575 void AwContentBrowserClient::OverrideWebkitPrefs(content::RenderViewHost* rvh,
    576                                                  const GURL& url,
    577                                                  WebPreferences* web_prefs) {
    578   if (!preferences_populater_.get()) {
    579     preferences_populater_ = make_scoped_ptr(native_factory_->
    580         CreateWebPreferencesPopulater());
    581   }
    582   preferences_populater_->PopulateFor(
    583       content::WebContents::FromRenderViewHost(rvh), web_prefs);
    584 }
    585 
    586 #if defined(VIDEO_HOLE)
    587 content::ExternalVideoSurfaceContainer*
    588 AwContentBrowserClient::OverrideCreateExternalVideoSurfaceContainer(
    589     content::WebContents* web_contents) {
    590   return native_factory_->CreateExternalVideoSurfaceContainer(web_contents);
    591 }
    592 #endif
    593 
    594 }  // namespace android_webview
    595