1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.net.ProxyProperties;
20import android.net.Uri;
21import android.os.Handler;
22import android.os.Message;
23import android.util.Log;
24
25import java.lang.ref.WeakReference;
26import java.util.HashMap;
27import java.util.Set;
28
29final class JWebCoreJavaBridge extends Handler {
30    // Identifier for the timer message.
31    private static final int TIMER_MESSAGE = 1;
32    // ID for servicing functionptr queue
33    private static final int FUNCPTR_MESSAGE = 2;
34    // Log system identifier.
35    private static final String LOGTAG = "webkit-timers";
36
37    // Native object pointer for interacting in native code.
38    private int mNativeBridge;
39    // Instant timer is used to implement a timer that needs to fire almost
40    // immediately.
41    private boolean mHasInstantTimer;
42
43    private boolean mTimerPaused;
44    private boolean mHasDeferredTimers;
45
46    // keep track of the main WebViewClassic attached to the current window so that we
47    // can get the proper Context.
48    private static WeakReference<WebViewClassic> sCurrentMainWebView =
49            new WeakReference<WebViewClassic>(null);
50
51    /* package */
52    static final int REFRESH_PLUGINS = 100;
53
54    private HashMap<String, String> mContentUriToFilePathMap;
55
56    /**
57     * Construct a new JWebCoreJavaBridge to interface with
58     * WebCore timers and cookies.
59     */
60    public JWebCoreJavaBridge() {
61        nativeConstructor();
62
63    }
64
65    @Override
66    protected void finalize() {
67        nativeFinalize();
68    }
69
70    static synchronized void setActiveWebView(WebViewClassic webview) {
71        if (sCurrentMainWebView.get() != null) {
72            // it is possible if there is a sub-WebView. Do nothing.
73            return;
74        }
75        sCurrentMainWebView = new WeakReference<WebViewClassic>(webview);
76    }
77
78    static synchronized void removeActiveWebView(WebViewClassic webview) {
79        if (sCurrentMainWebView.get() != webview) {
80            // it is possible if there is a sub-WebView. Do nothing.
81            return;
82        }
83        sCurrentMainWebView.clear();
84    }
85
86    /**
87     * Call native timer callbacks.
88     */
89    private void fireSharedTimer() {
90        // clear the flag so that sharedTimerFired() can set a new timer
91        mHasInstantTimer = false;
92        sharedTimerFired();
93    }
94
95    /**
96     * handleMessage
97     * @param msg The dispatched message.
98     *
99     * The only accepted message currently is TIMER_MESSAGE
100     */
101    @Override
102    public void handleMessage(Message msg) {
103        switch (msg.what) {
104            case TIMER_MESSAGE: {
105                if (mTimerPaused) {
106                    mHasDeferredTimers = true;
107                } else {
108                    fireSharedTimer();
109                }
110                break;
111            }
112            case FUNCPTR_MESSAGE:
113                nativeServiceFuncPtrQueue();
114                break;
115            case REFRESH_PLUGINS:
116                nativeUpdatePluginDirectories(PluginManager.getInstance(null)
117                        .getPluginDirectories(), ((Boolean) msg.obj)
118                        .booleanValue());
119                break;
120        }
121    }
122
123    // called from JNI side
124    private void signalServiceFuncPtrQueue() {
125        Message msg = obtainMessage(FUNCPTR_MESSAGE);
126        sendMessage(msg);
127    }
128
129    private native void nativeServiceFuncPtrQueue();
130
131    /**
132     * Pause all timers.
133     */
134    public void pause() {
135        if (!mTimerPaused) {
136            mTimerPaused = true;
137            mHasDeferredTimers = false;
138        }
139    }
140
141    /**
142     * Resume all timers.
143     */
144    public void resume() {
145        if (mTimerPaused) {
146           mTimerPaused = false;
147           if (mHasDeferredTimers) {
148               mHasDeferredTimers = false;
149               fireSharedTimer();
150           }
151        }
152    }
153
154    /**
155     * Set WebCore cache size.
156     * @param bytes The cache size in bytes.
157     */
158    public native void setCacheSize(int bytes);
159
160    /**
161     * Store a cookie string associated with a url.
162     * @param url The url to be used as a key for the cookie.
163     * @param value The cookie string to be stored.
164     */
165    private void setCookies(String url, String value) {
166        if (value.contains("\r") || value.contains("\n")) {
167            // for security reason, filter out '\r' and '\n' from the cookie
168            int size = value.length();
169            StringBuilder buffer = new StringBuilder(size);
170            int i = 0;
171            while (i != -1 && i < size) {
172                int ir = value.indexOf('\r', i);
173                int in = value.indexOf('\n', i);
174                int newi = (ir == -1) ? in : (in == -1 ? ir : (ir < in ? ir
175                        : in));
176                if (newi > i) {
177                    buffer.append(value.subSequence(i, newi));
178                } else if (newi == -1) {
179                    buffer.append(value.subSequence(i, size));
180                    break;
181                }
182                i = newi + 1;
183            }
184            value = buffer.toString();
185        }
186        CookieManager.getInstance().setCookie(url, value);
187    }
188
189    /**
190     * Retrieve the cookie string for the given url.
191     * @param url The resource's url.
192     * @return A String representing the cookies for the given resource url.
193     */
194    private String cookies(String url) {
195        return CookieManager.getInstance().getCookie(url);
196    }
197
198    /**
199     * Returns whether cookies are enabled or not.
200     */
201    private boolean cookiesEnabled() {
202        return CookieManager.getInstance().acceptCookie();
203    }
204
205    /**
206     * Returns an array of plugin directoies
207     */
208    private String[] getPluginDirectories() {
209        return PluginManager.getInstance(null).getPluginDirectories();
210    }
211
212    /**
213     * Returns the path of the plugin data directory
214     */
215    private String getPluginSharedDataDirectory() {
216        return PluginManager.getInstance(null).getPluginSharedDataDirectory();
217    }
218
219    /**
220     * setSharedTimer
221     * @param timemillis The relative time when the timer should fire
222     */
223    private void setSharedTimer(long timemillis) {
224        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
225
226        if (timemillis <= 0) {
227            // we don't accumulate the sharedTimer unless it is a delayed
228            // request. This way we won't flood the message queue with
229            // WebKit messages. This should improve the browser's
230            // responsiveness to key events.
231            if (mHasInstantTimer) {
232                return;
233            } else {
234                mHasInstantTimer = true;
235                Message msg = obtainMessage(TIMER_MESSAGE);
236                sendMessageDelayed(msg, timemillis);
237            }
238        } else {
239            Message msg = obtainMessage(TIMER_MESSAGE);
240            sendMessageDelayed(msg, timemillis);
241        }
242    }
243
244    /**
245     * Stop the shared timer.
246     */
247    private void stopSharedTimer() {
248        if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
249            Log.v(LOGTAG, "stopSharedTimer removing all timers");
250        }
251        removeMessages(TIMER_MESSAGE);
252        mHasInstantTimer = false;
253        mHasDeferredTimers = false;
254    }
255
256    private String[] getKeyStrengthList() {
257        return CertTool.getKeyStrengthList();
258    }
259
260    synchronized private String getSignedPublicKey(int index, String challenge,
261            String url) {
262        WebViewClassic current = sCurrentMainWebView.get();
263        if (current != null) {
264            // generateKeyPair expects organizations which we don't have. Ignore
265            // url.
266            return CertTool.getSignedPublicKey(
267                    current.getContext(), index, challenge);
268        } else {
269            Log.e(LOGTAG, "There is no active WebView for getSignedPublicKey");
270            return "";
271        }
272    }
273
274    // Called on the WebCore thread through JNI.
275    private String resolveFilePathForContentUri(String uri) {
276        if (mContentUriToFilePathMap != null) {
277            String fileName = mContentUriToFilePathMap.get(uri);
278            if (fileName != null) {
279                return fileName;
280            }
281        }
282
283        // Failsafe fallback to just use the last path segment.
284        // (See OpenableColumns documentation in the SDK)
285        Uri jUri = Uri.parse(uri);
286        return jUri.getLastPathSegment();
287    }
288
289    public void storeFilePathForContentUri(String path, String contentUri) {
290        if (mContentUriToFilePathMap == null) {
291            mContentUriToFilePathMap = new HashMap<String, String>();
292        }
293        mContentUriToFilePathMap.put(contentUri, path);
294    }
295
296    public void updateProxy(ProxyProperties proxyProperties) {
297        if (proxyProperties == null) {
298            nativeUpdateProxy("", "");
299            return;
300        }
301
302        String host = proxyProperties.getHost();
303        int port = proxyProperties.getPort();
304        if (port != 0)
305            host += ":" + port;
306
307        nativeUpdateProxy(host, proxyProperties.getExclusionList());
308    }
309
310    private native void nativeConstructor();
311    private native void nativeFinalize();
312    private native void sharedTimerFired();
313    private native void nativeUpdatePluginDirectories(String[] directories,
314            boolean reload);
315    public native void setNetworkOnLine(boolean online);
316    public native void setNetworkType(String type, String subtype);
317    public native void addPackageNames(Set<String> packageNames);
318    public native void addPackageName(String packageName);
319    public native void removePackageName(String packageName);
320    public native void nativeUpdateProxy(String newProxy, String exclusionList);
321}
322