1// Copyright 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
5package org.chromium.android_webview;
6
7import android.os.Handler;
8import android.os.Looper;
9import android.webkit.ValueCallback;
10
11import org.chromium.base.CalledByNative;
12import org.chromium.base.JNINamespace;
13
14/**
15 * AwCookieManager manages cookies according to RFC2109 spec.
16 *
17 * Methods in this class are thread safe.
18 */
19@JNINamespace("android_webview")
20public final class AwCookieManager {
21
22    // TODO(hjd): remove after landing android update to use new calls.
23    public void removeExpiredCookie() {
24        removeExpiredCookies();
25    }
26
27    // TODO(hjd): remove after landing android update to use new calls.
28    public void removeAllCookie() {
29        removeAllCookies();
30    }
31
32    // TODO(hjd): remove after landing android update to use new calls.
33    public void removeSessionCookie() {
34        removeSessionCookies();
35    }
36
37    /**
38     * Control whether cookie is enabled or disabled
39     * @param accept TRUE if accept cookie
40     */
41    public void setAcceptCookie(boolean accept) {
42        nativeSetShouldAcceptCookies(accept);
43    }
44
45    /**
46     * Return whether cookie is enabled
47     * @return TRUE if accept cookie
48     */
49    public boolean acceptCookie() {
50        return nativeGetShouldAcceptCookies();
51    }
52
53    /**
54     * Synchronous version of setCookie.
55     */
56    public void setCookie(String url, String value) {
57        nativeSetCookieSync(url, value);
58    }
59
60    /**
61     * Deprecated synchronous version of removeSessionCookies.
62     */
63    public void removeSessionCookies() {
64        nativeRemoveSessionCookiesSync();
65    }
66
67    /**
68     * Deprecated synchronous version of removeAllCookies.
69     */
70    public void removeAllCookies() {
71        nativeRemoveAllCookiesSync();
72    }
73
74    /**
75     * Set cookie for a given url. The old cookie with same host/path/name will
76     * be removed. The new cookie will be added if it is not expired or it does
77     * not have expiration which implies it is session cookie.
78     * @param url The url which cookie is set for.
79     * @param value The value for set-cookie: in http response header.
80     * @param callback A callback called with the success status after the cookie is set.
81     */
82    public void setCookie(final String url, final String value,
83            final ValueCallback<Boolean> callback) {
84        try {
85            nativeSetCookie(url, value, CookieCallback.convert(callback));
86        } catch (IllegalStateException e) {
87            throw new IllegalStateException(
88                    "SetCookie must be called on a thread with a running Looper.");
89        }
90    }
91
92    /**
93     * Get cookie(s) for a given url so that it can be set to "cookie:" in http
94     * request header.
95     * @param url The url needs cookie
96     * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
97     */
98    public String getCookie(final String url) {
99        String cookie = nativeGetCookie(url.toString());
100        // Return null if the string is empty to match legacy behavior
101        return cookie == null || cookie.trim().isEmpty() ? null : cookie;
102    }
103
104    /**
105     * Remove all session cookies, the cookies without an expiration date.
106     * The value of the callback is true iff at least one cookie was removed.
107     * @param callback A callback called after the cookies (if any) are removed.
108     */
109    public void removeSessionCookies(ValueCallback<Boolean> callback) {
110        try {
111            nativeRemoveSessionCookies(CookieCallback.convert(callback));
112        } catch (IllegalStateException e) {
113            throw new IllegalStateException(
114                    "removeSessionCookies must be called on a thread with a running Looper.");
115        }
116    }
117
118    /**
119     * Remove all cookies.
120     * The value of the callback is true iff at least one cookie was removed.
121     * @param callback A callback called after the cookies (if any) are removed.
122     */
123    public void removeAllCookies(ValueCallback<Boolean> callback) {
124        try {
125            nativeRemoveAllCookies(CookieCallback.convert(callback));
126        } catch (IllegalStateException e) {
127            throw new IllegalStateException(
128                    "removeAllCookies must be called on a thread with a running Looper.");
129        }
130    }
131
132    /**
133     *  Return true if there are stored cookies.
134     */
135    public boolean hasCookies() {
136        return nativeHasCookies();
137    }
138
139    /**
140     * Remove all expired cookies
141     */
142    public void removeExpiredCookies() {
143        nativeRemoveExpiredCookies();
144    }
145
146    public void flushCookieStore() {
147        nativeFlushCookieStore();
148    }
149
150    /**
151     * Whether cookies are accepted for file scheme URLs.
152     */
153    public boolean allowFileSchemeCookies() {
154        return nativeAllowFileSchemeCookies();
155    }
156
157    /**
158     * Sets whether cookies are accepted for file scheme URLs.
159     *
160     * Use of cookies with file scheme URLs is potentially insecure. Do not use this feature unless
161     * you can be sure that no unintentional sharing of cookie data can take place.
162     * <p>
163     * Note that calls to this method will have no effect if made after a WebView or CookieManager
164     * instance has been created.
165     */
166    public void setAcceptFileSchemeCookies(boolean accept) {
167        nativeSetAcceptFileSchemeCookies(accept);
168    }
169
170    @CalledByNative
171    public static void invokeBooleanCookieCallback(CookieCallback<Boolean> callback,
172            boolean result) {
173        callback.onReceiveValue(result);
174    }
175
176    /**
177     * CookieCallback is a bridge that knows how to call a ValueCallback on its original thread.
178     * We need to arrange for the users ValueCallback#onReceiveValue to be called on the original
179     * thread after the work is done. When the API is called we construct a CookieCallback which
180     * remembers the handler of the current thread. Later the native code uses
181     * invokeBooleanCookieCallback to call CookieCallback#onReceiveValue which posts a Runnable
182     * on the handler of the original thread which in turn calls ValueCallback#onReceiveValue.
183     */
184    private static class CookieCallback<T> {
185        ValueCallback<T> mCallback;
186        Handler mHandler;
187
188        public CookieCallback(ValueCallback<T> callback, Handler handler) {
189            mCallback = callback;
190            mHandler = handler;
191        }
192
193       public static<T> CookieCallback<T> convert(ValueCallback<T> callback) throws
194                IllegalStateException {
195            if (callback == null) {
196                return null;
197            }
198            if (Looper.myLooper() == null) {
199              throw new IllegalStateException(
200                  "CookieCallback.convert should be called on a thread with a running Looper.");
201            }
202            return new CookieCallback<T>(callback, new Handler());
203        }
204
205        public void onReceiveValue(final T t) {
206            mHandler.post(new Runnable() {
207                @Override
208                public void run() {
209                    mCallback.onReceiveValue(t);
210                }
211            });
212        }
213    }
214
215    private native void nativeSetShouldAcceptCookies(boolean accept);
216    private native boolean nativeGetShouldAcceptCookies();
217
218    private native void nativeSetCookie(String url, String value,
219            CookieCallback<Boolean> callback);
220    private native void nativeSetCookieSync(String url, String value);
221    private native String nativeGetCookie(String url);
222
223    private native void nativeRemoveSessionCookies(CookieCallback<Boolean> callback);
224    private native void nativeRemoveSessionCookiesSync();
225    private native void nativeRemoveAllCookies(CookieCallback<Boolean> callback);
226    private native void nativeRemoveAllCookiesSync();
227    private native void nativeRemoveExpiredCookies();
228    private native void nativeFlushCookieStore();
229
230    private native boolean nativeHasCookies();
231
232    private native boolean nativeAllowFileSchemeCookies();
233    private native void nativeSetAcceptFileSchemeCookies(boolean accept);
234}
235