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