Commit db1404ea authored by Evan Stade's avatar Evan Stade Committed by Commit Bot

WebLayer: allow client to handle downloads.

As with WebView, the client is responsible for handling download
requests. The Android shell client simply uses Android's
DownloadManager.

Bug: none
Change-Id: I0204a2101f675c35682f0c75cbe34a506c7b5e2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1862121
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707177}
parent d47ac152
......@@ -60,6 +60,7 @@ jumbo_static_library("weblayer_lib") {
"common/content_client_impl.h",
"public/browser_controller.h",
"public/browser_observer.h",
"public/download_delegate.h",
"public/fullscreen_delegate.h",
"public/main.h",
"public/navigation.h",
......@@ -154,6 +155,8 @@ jumbo_static_library("weblayer_lib") {
"browser/browser_observer_proxy.h",
"browser/content_view_render_view.cc",
"browser/content_view_render_view.h",
"browser/download_delegate_proxy.cc",
"browser/download_delegate_proxy.h",
"browser/fullscreen_delegate_proxy.cc",
"browser/fullscreen_delegate_proxy.h",
"browser/top_controls_container_view.cc",
......
......@@ -13,6 +13,7 @@
#include "weblayer/browser/navigation_controller_impl.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/public/browser_observer.h"
#include "weblayer/public/download_delegate.h"
#include "weblayer/public/fullscreen_delegate.h"
#if !defined(OS_ANDROID)
......@@ -75,6 +76,10 @@ BrowserControllerImpl* BrowserControllerImpl::FromWebContents(
->controller;
}
void BrowserControllerImpl::SetDownloadDelegate(DownloadDelegate* delegate) {
download_delegate_ = delegate;
}
void BrowserControllerImpl::SetFullscreenDelegate(
FullscreenDelegate* delegate) {
if (delegate == fullscreen_delegate_)
......
......@@ -56,10 +56,12 @@ class BrowserControllerImpl : public BrowserController,
jlong native_top_controls_container_view);
#endif
DownloadDelegate* download_delegate() { return download_delegate_; }
FullscreenDelegate* fullscreen_delegate() { return fullscreen_delegate_; }
private:
// BrowserController implementation:
// BrowserController:
void SetDownloadDelegate(DownloadDelegate* delegate) override;
void SetFullscreenDelegate(FullscreenDelegate* delegate) override;
void AddObserver(BrowserObserver* observer) override;
void RemoveObserver(BrowserObserver* observer) override;
......@@ -68,7 +70,7 @@ class BrowserControllerImpl : public BrowserController,
void AttachToView(views::WebView* web_view) override;
#endif
// content::WebContentsDelegate implementation:
// content::WebContentsDelegate:
void LoadingStateChanged(content::WebContents* source,
bool to_different_document) override;
void LoadProgressChanged(content::WebContents* source,
......@@ -89,7 +91,7 @@ class BrowserControllerImpl : public BrowserController,
blink::mojom::DisplayMode GetDisplayMode(
const content::WebContents* web_contents) override;
// content::WebContentsObserver implementation:
// content::WebContentsObserver:
void DidFirstVisuallyNonEmptyPaint() override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
......@@ -97,6 +99,7 @@ class BrowserControllerImpl : public BrowserController,
// Called from closure supplied to delegate to exit fullscreen.
void OnExitFullscreen();
DownloadDelegate* download_delegate_ = nullptr;
FullscreenDelegate* fullscreen_delegate_ = nullptr;
ProfileImpl* profile_;
std::unique_ptr<content::WebContents> web_contents_;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "weblayer/browser/download_delegate_proxy.h"
#include "base/android/jni_string.h"
#include "url/gurl.h"
#include "weblayer/browser/browser_controller_impl.h"
#include "weblayer/browser/java/jni/DownloadDelegateProxy_jni.h"
using base::android::AttachCurrentThread;
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
namespace weblayer {
DownloadDelegateProxy::DownloadDelegateProxy(
JNIEnv* env,
jobject obj,
BrowserController* browser_controller)
: browser_controller_(browser_controller), java_delegate_(env, obj) {
browser_controller_->SetDownloadDelegate(this);
}
DownloadDelegateProxy::~DownloadDelegateProxy() {
browser_controller_->SetDownloadDelegate(nullptr);
}
void DownloadDelegateProxy::DownloadRequested(
const GURL& url,
const std::string& user_agent,
const std::string& content_disposition,
const std::string& mime_type,
int64_t content_length) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> jstring_url(
ConvertUTF8ToJavaString(env, url.spec()));
ScopedJavaLocalRef<jstring> jstring_user_agent(
ConvertUTF8ToJavaString(env, user_agent));
ScopedJavaLocalRef<jstring> jstring_content_disposition(
ConvertUTF8ToJavaString(env, content_disposition));
ScopedJavaLocalRef<jstring> jstring_mime_type(
ConvertUTF8ToJavaString(env, mime_type));
Java_DownloadDelegateProxy_downloadRequested(
env, java_delegate_, jstring_url, jstring_user_agent,
jstring_content_disposition, jstring_mime_type, content_length);
}
static jlong JNI_DownloadDelegateProxy_CreateDownloadDelegateProxy(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& proxy,
jlong browser_controller) {
return reinterpret_cast<jlong>(new DownloadDelegateProxy(
env, proxy,
reinterpret_cast<BrowserControllerImpl*>(browser_controller)));
}
static void JNI_DownloadDelegateProxy_DeleteDownloadDelegateProxy(JNIEnv* env,
jlong proxy) {
delete reinterpret_cast<DownloadDelegateProxy*>(proxy);
}
} // namespace weblayer
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WEBLAYER_BROWSER_DOWNLOAD_DELEGATE_PROXY_H_
#define WEBLAYER_BROWSER_DOWNLOAD_DELEGATE_PROXY_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/callback.h"
#include "base/macros.h"
#include "weblayer/public/download_delegate.h"
namespace weblayer {
class BrowserController;
// Forwards DownloadDelegate calls to the java-side DownloadDelegateProxy.
class DownloadDelegateProxy : public DownloadDelegate {
public:
DownloadDelegateProxy(JNIEnv* env,
jobject obj,
BrowserController* browser_controller);
~DownloadDelegateProxy() override;
// DownloadDelegate:
void DownloadRequested(const GURL& url,
const std::string& user_agent,
const std::string& content_disposition,
const std::string& mime_type,
int64_t content_length) override;
private:
BrowserController* browser_controller_;
base::android::ScopedJavaGlobalRef<jobject> java_delegate_;
DISALLOW_COPY_AND_ASSIGN(DownloadDelegateProxy);
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_DOWNLOAD_DELEGATE_PROXY_H_
......@@ -23,6 +23,7 @@ android_library("java") {
"org/chromium/weblayer_private/BrowserObserverProxy.java",
"org/chromium/weblayer_private/ContentView.java",
"org/chromium/weblayer_private/ContentViewRenderView.java",
"org/chromium/weblayer_private/DownloadDelegateProxy.java",
"org/chromium/weblayer_private/FullscreenDelegateProxy.java",
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
......@@ -52,6 +53,7 @@ generate_jni("jni") {
"org/chromium/weblayer_private/BrowserControllerImpl.java",
"org/chromium/weblayer_private/BrowserObserverProxy.java",
"org/chromium/weblayer_private/ContentViewRenderView.java",
"org/chromium/weblayer_private/DownloadDelegateProxy.java",
"org/chromium/weblayer_private/FullscreenDelegateProxy.java",
"org/chromium/weblayer_private/NavigationControllerImpl.java",
"org/chromium/weblayer_private/NavigationImpl.java",
......@@ -80,6 +82,7 @@ android_aidl("aidl") {
"org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl",
"org/chromium/weblayer_private/aidl/IChildProcessService.aidl",
"org/chromium/weblayer_private/aidl/IClientNavigation.aidl",
"org/chromium/weblayer_private/aidl/IDownloadDelegateClient.aidl",
"org/chromium/weblayer_private/aidl/IFullscreenDelegateClient.aidl",
"org/chromium/weblayer_private/aidl/INavigation.aidl",
"org/chromium/weblayer_private/aidl/INavigationController.aidl",
......
......@@ -21,6 +21,7 @@ import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
import org.chromium.weblayer_private.aidl.IDownloadDelegateClient;
import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
import org.chromium.weblayer_private.aidl.INavigationControllerClient;
import org.chromium.weblayer_private.aidl.IObjectWrapper;
......@@ -43,6 +44,7 @@ public final class BrowserControllerImpl extends IBrowserController.Stub {
private WebContents mWebContents;
private BrowserObserverProxy mBrowserObserverProxy;
private NavigationControllerImpl mNavigationController;
private DownloadDelegateProxy mDownloadDelegateProxy;
private FullscreenDelegateProxy mFullscreenDelegateProxy;
private static class InternalAccessDelegateImpl
......@@ -129,6 +131,21 @@ public final class BrowserControllerImpl extends IBrowserController.Stub {
mBrowserObserverProxy = new BrowserObserverProxy(mNativeBrowserController, client);
}
@Override
public void setDownloadDelegateClient(IDownloadDelegateClient client) {
if (client != null) {
if (mDownloadDelegateProxy == null) {
mDownloadDelegateProxy =
new DownloadDelegateProxy(mNativeBrowserController, client);
} else {
mDownloadDelegateProxy.setClient(client);
}
} else if (mDownloadDelegateProxy != null) {
mDownloadDelegateProxy.destroy();
mDownloadDelegateProxy = null;
}
}
@Override
public void setFullscreenDelegateClient(IFullscreenDelegateClient client) {
if (client != null) {
......@@ -153,6 +170,10 @@ public final class BrowserControllerImpl extends IBrowserController.Stub {
mBrowserObserverProxy.destroy();
mBrowserObserverProxy = null;
}
if (mDownloadDelegateProxy != null) {
mDownloadDelegateProxy.destroy();
mDownloadDelegateProxy = null;
}
if (mFullscreenDelegateProxy != null) {
mFullscreenDelegateProxy.destroy();
mFullscreenDelegateProxy = null;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.weblayer_private;
import android.os.RemoteException;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.weblayer_private.aidl.APICallException;
import org.chromium.weblayer_private.aidl.IDownloadDelegateClient;
/**
* Owns the c++ DownloadDelegateProxy class, which is responsible for forwarding all
* DownloadDelegate delegate calls to this class, which in turn forwards to the
* DownloadDelegateClient.
*/
@JNINamespace("weblayer")
public final class DownloadDelegateProxy {
private long mNativeDownloadDelegateProxy;
private IDownloadDelegateClient mClient;
DownloadDelegateProxy(long browserController, IDownloadDelegateClient client) {
assert client != null;
mClient = client;
mNativeDownloadDelegateProxy =
DownloadDelegateProxyJni.get().createDownloadDelegateProxy(this, browserController);
}
public void setClient(IDownloadDelegateClient client) {
assert client != null;
mClient = client;
}
public void destroy() {
DownloadDelegateProxyJni.get().deleteDownloadDelegateProxy(mNativeDownloadDelegateProxy);
mNativeDownloadDelegateProxy = 0;
}
@CalledByNative
private void downloadRequested(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
try {
mClient.downloadRequested(url, userAgent, contentDisposition, mimetype, contentLength);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
@NativeMethods
interface Natives {
long createDownloadDelegateProxy(DownloadDelegateProxy proxy, long browserController);
void deleteDownloadDelegateProxy(long proxy);
}
}
......@@ -14,5 +14,7 @@ interface IBrowserController {
INavigationController createNavigationController(in INavigationControllerClient client) = 1;
void setFullscreenDelegateClient(in IFullscreenDelegateClient client) = 2;
void setDownloadDelegateClient(IDownloadDelegateClient client) = 2;
void setFullscreenDelegateClient(in IFullscreenDelegateClient client) = 3;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.weblayer_private.aidl;
/**
* Used to forward download requests to the client.
*/
interface IDownloadDelegateClient {
void downloadRequested(in String url, in String userAgent, in String contentDisposition, in String mimetype, long contentLength) = 0;
}
......@@ -6,7 +6,10 @@
#include "build/build_config.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/resource_context.h"
#include "weblayer/browser/browser_controller_impl.h"
#include "weblayer/public/download_delegate.h"
#if defined(OS_ANDROID)
#include "base/android/jni_string.h"
......@@ -26,6 +29,38 @@ class ResourceContextImpl : public content::ResourceContext {
DISALLOW_COPY_AND_ASSIGN(ResourceContextImpl);
};
class DownloadManagerDelegateImpl : public content::DownloadManagerDelegate {
public:
DownloadManagerDelegateImpl() = default;
~DownloadManagerDelegateImpl() override = default;
bool InterceptDownloadIfApplicable(
const GURL& url,
const std::string& user_agent,
const std::string& content_disposition,
const std::string& mime_type,
const std::string& request_origin,
int64_t content_length,
bool is_transient,
content::WebContents* web_contents) override {
// If there's no DownloadDelegate, the download is simply dropped.
auto* browser = BrowserControllerImpl::FromWebContents(web_contents);
if (!browser)
return true;
DownloadDelegate* delegate = browser->download_delegate();
if (!delegate)
return true;
delegate->DownloadRequested(url, user_agent, content_disposition, mime_type,
content_length);
return true;
}
private:
DISALLOW_COPY_AND_ASSIGN(DownloadManagerDelegateImpl);
};
} // namespace
class ProfileImpl::BrowserContextImpl : public content::BrowserContext {
......@@ -50,7 +85,7 @@ class ProfileImpl::BrowserContextImpl : public content::BrowserContext {
bool IsOffTheRecord() override { return path_.empty(); }
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override {
return nullptr;
return &download_delegate_;
}
content::ResourceContext* GetResourceContext() override {
......@@ -108,6 +143,7 @@ class ProfileImpl::BrowserContextImpl : public content::BrowserContext {
private:
base::FilePath path_;
std::unique_ptr<ResourceContextImpl> resource_context_;
DownloadManagerDelegateImpl download_delegate_;
DISALLOW_COPY_AND_ASSIGN(BrowserContextImpl);
};
......
......@@ -17,6 +17,7 @@ class WebView;
namespace weblayer {
class BrowserObserver;
class DownloadDelegate;
class FullscreenDelegate;
class Profile;
class NavigationController;
......@@ -32,6 +33,9 @@ class BrowserController {
virtual ~BrowserController() {}
// Sets the DownloadDelegate. If none is set, downloads will be dropped.
virtual void SetDownloadDelegate(DownloadDelegate* delegate) = 0;
// Sets the FullscreenDelegate. Setting a non-null value implicitly enables
// fullscreen.
virtual void SetFullscreenDelegate(FullscreenDelegate* delegate) = 0;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WEBLAYER_PUBLIC_DOWNLOAD_DELEGATE_H_
#define WEBLAYER_PUBLIC_DOWNLOAD_DELEGATE_H_
#include <string>
class GURL;
namespace weblayer {
// An interface that allows clients to handle download requests originating in
// the browser.
class DownloadDelegate {
public:
// A download of |url| has been requested with the specified details. If
// ignored, the download will be dropped.
virtual void DownloadRequested(const GURL& url,
const std::string& user_agent,
const std::string& content_disposition,
const std::string& mime_type,
int64_t content_length) = 0;
protected:
virtual ~DownloadDelegate() {}
};
} // namespace weblayer
#endif // WEBLAYER_PUBLIC_DOWNLOAD_DELEGATE_H_
......@@ -27,6 +27,7 @@ template("weblayer_java") {
"org/chromium/weblayer/BrowserFragmentController.java",
"org/chromium/weblayer/BrowserObserver.java",
"org/chromium/weblayer/Callback.java",
"org/chromium/weblayer/DownloadDelegate.java",
"org/chromium/weblayer/FullscreenDelegate.java",
"org/chromium/weblayer/ListenableFuture.java",
"org/chromium/weblayer/ListenableResult.java",
......
......@@ -11,6 +11,7 @@ import android.webkit.ValueCallback;
import org.chromium.weblayer_private.aidl.APICallException;
import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
import org.chromium.weblayer_private.aidl.IDownloadDelegateClient;
import org.chromium.weblayer_private.aidl.IFullscreenDelegateClient;
import org.chromium.weblayer_private.aidl.IObjectWrapper;
import org.chromium.weblayer_private.aidl.ObjectWrapper;
......@@ -20,6 +21,7 @@ public final class BrowserController {
private FullscreenDelegateClientImpl mFullscreenDelegateClient;
private final NavigationController mNavigationController;
private final ObserverList<BrowserObserver> mObservers;
private DownloadDelegateClientImpl mDownloadDelegateClient;
BrowserController(IBrowserController impl) {
mImpl = impl;
......@@ -33,6 +35,20 @@ public final class BrowserController {
mNavigationController = NavigationController.create(mImpl);
}
public void setDownloadDelegate(DownloadDelegate delegate) {
try {
if (delegate != null) {
mDownloadDelegateClient = new DownloadDelegateClientImpl(delegate);
mImpl.setDownloadDelegateClient(mDownloadDelegateClient);
} else {
mDownloadDelegateClient = null;
mImpl.setDownloadDelegateClient(null);
}
} catch (RemoteException e) {
throw new APICallException(e);
}
}
public void setFullscreenDelegate(FullscreenDelegate delegate) {
try {
if (delegate != null) {
......@@ -46,6 +62,10 @@ public final class BrowserController {
}
}
public DownloadDelegate getDownloadDelegate() {
return mDownloadDelegateClient != null ? mDownloadDelegateClient.getDelegate() : null;
}
public FullscreenDelegate getFullscreenDelegate() {
return mFullscreenDelegateClient != null ? mFullscreenDelegateClient.getDelegate() : null;
}
......@@ -95,6 +115,25 @@ public final class BrowserController {
}
}
private final class DownloadDelegateClientImpl extends IDownloadDelegateClient.Stub {
private final DownloadDelegate mDelegate;
DownloadDelegateClientImpl(DownloadDelegate delegate) {
mDelegate = delegate;
}
public DownloadDelegate getDelegate() {
return mDelegate;
}
@Override
public void downloadRequested(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
mDelegate.downloadRequested(
url, userAgent, contentDisposition, mimetype, contentLength);
}
}
private final class FullscreenDelegateClientImpl extends IFullscreenDelegateClient.Stub {
private FullscreenDelegate mDelegate;
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.weblayer;
/**
* An interface that allows clients to handle download requests originating in the browser.
*/
public abstract class DownloadDelegate {
/**
* A download of has been requested with the specified details.
*
* @param url the target that should be downloaded
* @param userAgent the user agent to be used for the download
* @param contentDisposition content-disposition http header, if present
* @param mimetype the mimetype of the content reported by the server
* @param contentLength the file size reported by the server
*/
public abstract void downloadRequested(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength);
}
......@@ -4,6 +4,7 @@
package org.chromium.weblayer.shell;
import android.app.DownloadManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
......@@ -30,6 +31,7 @@ import org.chromium.weblayer.BrowserController;
import org.chromium.weblayer.BrowserFragment;
import org.chromium.weblayer.BrowserFragmentController;
import org.chromium.weblayer.BrowserObserver;
import org.chromium.weblayer.DownloadDelegate;
import org.chromium.weblayer.FullscreenDelegate;
import org.chromium.weblayer.NavigationController;
import org.chromium.weblayer.Profile;
......@@ -196,6 +198,16 @@ public class WebLayerShellActivity extends FragmentActivity {
mLoadProgressBar.setProgress((int) Math.round(100 * progress));
}
});
mBrowserController.setDownloadDelegate(new DownloadDelegate() {
@Override
public void downloadRequested(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
getSystemService(DownloadManager.class).enqueue(request);
}
});
}
private BrowserFragment getOrCreateBrowserFragment(Bundle savedInstanceState) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment