1 // Copyright 2020 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 package org.chromium.weblayer; 6 7 import android.net.Uri; 8 import android.os.RemoteException; 9 import android.webkit.ValueCallback; 10 11 import androidx.annotation.NonNull; 12 import androidx.annotation.Nullable; 13 14 import org.chromium.weblayer_private.interfaces.APICallException; 15 import org.chromium.weblayer_private.interfaces.ICookieChangedCallbackClient; 16 import org.chromium.weblayer_private.interfaces.ICookieManager; 17 import org.chromium.weblayer_private.interfaces.IProfile; 18 import org.chromium.weblayer_private.interfaces.ObjectWrapper; 19 import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; 20 21 /** 22 * Manages cookies for a WebLayer profile. 23 * 24 * @since 83 25 */ 26 public class CookieManager { 27 private final ICookieManager mImpl; 28 create(IProfile profile)29 static CookieManager create(IProfile profile) { 30 try { 31 return new CookieManager(profile.getCookieManager()); 32 } catch (RemoteException e) { 33 throw new APICallException(e); 34 } 35 } 36 37 // Constructor for test mocking. CookieManager()38 protected CookieManager() { 39 mImpl = null; 40 } 41 CookieManager(ICookieManager impl)42 private CookieManager(ICookieManager impl) { 43 mImpl = impl; 44 } 45 46 /** 47 * Sets a cookie for the given URL. 48 * 49 * @param uri the URI for which the cookie is to be set. 50 * @param value the cookie string, using the format of the 'Set-Cookie' HTTP response header. 51 * @param callback a callback to be executed when the cookie has been set, or on failure. Called 52 * with true if the cookie is set successfully, and false if the cookie is not set for 53 * security reasons. 54 * 55 * @throws IllegalArgumentException if the cookie is invalid. 56 */ setCookie( @onNull Uri uri, @NonNull String value, @Nullable Callback<Boolean> callback)57 public void setCookie( 58 @NonNull Uri uri, @NonNull String value, @Nullable Callback<Boolean> callback) { 59 ThreadCheck.ensureOnUiThread(); 60 try { 61 ValueCallback<Boolean> valueCallback = (Boolean result) -> { 62 if (callback != null) { 63 callback.onResult(result); 64 } 65 }; 66 if (!mImpl.setCookie(uri.toString(), value, ObjectWrapper.wrap(valueCallback))) { 67 throw new IllegalArgumentException("Invalid cookie: " + value); 68 } 69 } catch (RemoteException e) { 70 throw new APICallException(e); 71 } 72 } 73 74 /** 75 * Gets the cookies for the given URL. 76 * 77 * @param uri the URI to get cookies for. 78 * @param callback a callback to be executed with the cookie value in the format of the 'Cookie' 79 * HTTP request header. If there is no cookie, this will be called with an empty string. 80 */ getCookie(@onNull Uri uri, @NonNull Callback<String> callback)81 public void getCookie(@NonNull Uri uri, @NonNull Callback<String> callback) { 82 ThreadCheck.ensureOnUiThread(); 83 try { 84 ValueCallback<String> valueCallback = (String result) -> { 85 callback.onResult(result); 86 }; 87 mImpl.getCookie(uri.toString(), ObjectWrapper.wrap(valueCallback)); 88 } catch (RemoteException e) { 89 throw new APICallException(e); 90 } 91 } 92 93 /** 94 * Adds a callback to listen for changes to cookies for the given URI. 95 * 96 * @param uri the URI to listen to cookie changes on. 97 * @param name the name of the cookie to listen for changes on. Can be null to listen for 98 * changes on all cookies. 99 * @param callback a callback that will be notified on cookie changes. 100 * @return a Runnable which will unregister the callback from listening to cookie changes. 101 * @throws IllegalArgumentException if the cookie name is an empty string. 102 */ 103 @NonNull addCookieChangedCallback( @onNull Uri uri, @Nullable String name, @NonNull CookieChangedCallback callback)104 public Runnable addCookieChangedCallback( 105 @NonNull Uri uri, @Nullable String name, @NonNull CookieChangedCallback callback) { 106 ThreadCheck.ensureOnUiThread(); 107 if (name != null && name.isEmpty()) { 108 throw new IllegalArgumentException( 109 "Name cannot be empty, use null to listen for all cookie changes."); 110 } 111 try { 112 return ObjectWrapper.unwrap(mImpl.addCookieChangedCallback(uri.toString(), name, 113 new CookieChangedCallbackClientImpl(callback)), 114 Runnable.class); 115 } catch (RemoteException e) { 116 throw new APICallException(e); 117 } 118 } 119 120 private static final class CookieChangedCallbackClientImpl 121 extends ICookieChangedCallbackClient.Stub { 122 private final CookieChangedCallback mCallback; 123 CookieChangedCallbackClientImpl(CookieChangedCallback callback)124 CookieChangedCallbackClientImpl(CookieChangedCallback callback) { 125 mCallback = callback; 126 } 127 128 @Override onCookieChanged(String cookie, @CookieChangeCause int cause)129 public void onCookieChanged(String cookie, @CookieChangeCause int cause) { 130 StrictModeWorkaround.apply(); 131 mCallback.onCookieChanged(cookie, cause); 132 } 133 } 134 } 135