blob: d8f08b2977b6ea33f9d7bd96648695b690800bdb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Grace Kloba11843132009-10-08 20:48:09 -070019import android.app.ActivityManager;
Patrick Scott11f57382010-03-25 11:18:59 -040020import android.content.ComponentCallbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
22import android.content.res.AssetManager;
Patrick Scott11f57382010-03-25 11:18:59 -040023import android.content.res.Configuration;
John Reckd6662b52011-02-16 14:21:21 -080024import android.content.res.Resources;
25import android.content.res.Resources.NotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.graphics.Bitmap;
27import android.net.ParseException;
Leon Scroggins70ca3c22009-10-02 15:58:55 -040028import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.net.WebAddress;
Iain Merrick83d4a232010-11-12 14:11:16 +000030import android.net.http.ErrorStrings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.net.http.SslCertificate;
Huahui Wuad053ce2010-12-08 15:24:55 -080032import android.net.http.SslError;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.Handler;
34import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36import android.util.TypedValue;
Patrick Scottf06364b2009-12-02 08:57:09 -050037import android.view.Surface;
Dianne Hackborn6dd005b2011-07-18 13:22:50 -070038import android.view.ViewRootImpl;
Patrick Scottdbde4252010-03-24 15:47:17 -040039import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
41import junit.framework.Assert;
42
Kristian Monsendba35c02010-08-19 20:22:53 +010043import java.io.IOException;
Leon Scroggins70ca3c22009-10-02 15:58:55 -040044import java.io.InputStream;
Patrick Scott11f57382010-03-25 11:18:59 -040045import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.net.URLEncoder;
Brian Carlstroma1477592011-02-11 13:39:56 -080047import java.nio.charset.Charsets;
48import java.security.PrivateKey;
49import java.security.cert.CertificateEncodingException;
Huahui Wuad053ce2010-12-08 15:24:55 -080050import java.security.cert.X509Certificate;
Patrick Scott11f57382010-03-25 11:18:59 -040051import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.util.HashMap;
Steve Block2cb978e2011-07-01 17:55:51 +010053import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import java.util.Iterator;
Steve Block2cb978e2011-07-01 17:55:51 +010055import java.util.Map;
56import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
Huahui Wuad053ce2010-12-08 15:24:55 -080058import org.apache.harmony.security.provider.cert.X509CertImpl;
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060class BrowserFrame extends Handler {
61
62 private static final String LOGTAG = "webkit";
63
64 /**
65 * Cap the number of LoadListeners that will be instantiated, so
66 * we don't blow the GREF count. Attempting to queue more than
67 * this many requests will prompt an error() callback on the
68 * request's LoadListener
69 */
70 private final static int MAX_OUTSTANDING_REQUESTS = 300;
71
72 private final CallbackProxy mCallbackProxy;
73 private final WebSettings mSettings;
74 private final Context mContext;
75 private final WebViewDatabase mDatabase;
76 private final WebViewCore mWebViewCore;
77 /* package */ boolean mLoadInitFromJava;
78 private int mLoadType;
79 private boolean mFirstLayoutDone = true;
80 private boolean mCommitted = true;
Leon Clarkec0b778e2010-03-15 15:27:02 +000081 // Flag for blocking messages. This is used during destroy() so
82 // that if the UI thread posts any messages after the message
83 // queue has been cleared,they are ignored.
84 private boolean mBlockMessages = false;
Shimeng (Simon) Wang2edfcd52010-10-11 13:39:17 -070085 private int mOrientation = -1;
Kristian Monsenedb20ac2010-06-16 16:00:37 +010086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 // Is this frame the main frame?
88 private boolean mIsMainFrame;
89
90 // Attached Javascript interfaces
Steve Block2cb978e2011-07-01 17:55:51 +010091 private Map<String, Object> mJavaScriptObjects;
92 private Set<Object> mRemovedJavaScriptObjects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Huahui Wuc7939b12011-01-26 22:11:02 -080094 // Key store handler when Chromium HTTP stack is used.
95 private KeyStoreHandler mKeyStoreHandler = null;
96
Narayan Kamath9497c5f2011-02-22 12:05:34 +000097 // Implementation of the searchbox API.
98 private final SearchBoxImpl mSearchBox;
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 // message ids
101 // a message posted when a frame loading is completed
102 static final int FRAME_COMPLETED = 1001;
Patrick Scottf06364b2009-12-02 08:57:09 -0500103 // orientation change message
104 static final int ORIENTATION_CHANGED = 1002;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 // a message posted when the user decides the policy
106 static final int POLICY_FUNCTION = 1003;
107
108 // Note: need to keep these in sync with FrameLoaderTypes.h in native
109 static final int FRAME_LOADTYPE_STANDARD = 0;
110 static final int FRAME_LOADTYPE_BACK = 1;
111 static final int FRAME_LOADTYPE_FORWARD = 2;
112 static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3;
113 static final int FRAME_LOADTYPE_RELOAD = 4;
114 static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5;
115 static final int FRAME_LOADTYPE_SAME = 6;
116 static final int FRAME_LOADTYPE_REDIRECT = 7;
117 static final int FRAME_LOADTYPE_REPLACE = 8;
118
119 // A progress threshold to switch from history Picture to live Picture
120 private static final int TRANSITION_SWITCH_THRESHOLD = 75;
121
122 // This is a field accessed by native code as well as package classes.
123 /*package*/ int mNativeFrame;
124
125 // Static instance of a JWebCoreJavaBridge to handle timer and cookie
126 // requests from WebCore.
127 static JWebCoreJavaBridge sJavaBridge;
128
Patrick Scott11f57382010-03-25 11:18:59 -0400129 private static class ConfigCallback implements ComponentCallbacks {
130 private final ArrayList<WeakReference<Handler>> mHandlers =
131 new ArrayList<WeakReference<Handler>>();
132 private final WindowManager mWindowManager;
133
134 ConfigCallback(WindowManager wm) {
135 mWindowManager = wm;
136 }
137
138 public synchronized void addHandler(Handler h) {
139 // No need to ever remove a Handler. If the BrowserFrame is
140 // destroyed, it will be collected and the WeakReference set to
141 // null. If it happens to still be around during a configuration
142 // change, the message will be ignored.
143 mHandlers.add(new WeakReference<Handler>(h));
144 }
145
146 public void onConfigurationChanged(Configuration newConfig) {
147 if (mHandlers.size() == 0) {
148 return;
149 }
150 int orientation =
151 mWindowManager.getDefaultDisplay().getOrientation();
152 switch (orientation) {
153 case Surface.ROTATION_90:
154 orientation = 90;
155 break;
156 case Surface.ROTATION_180:
157 orientation = 180;
158 break;
159 case Surface.ROTATION_270:
160 orientation = -90;
161 break;
162 case Surface.ROTATION_0:
163 orientation = 0;
164 break;
165 default:
166 break;
167 }
168 synchronized (this) {
169 // Create a list of handlers to remove. Go ahead and make it
170 // the same size to avoid resizing.
171 ArrayList<WeakReference> handlersToRemove =
172 new ArrayList<WeakReference>(mHandlers.size());
173 for (WeakReference<Handler> wh : mHandlers) {
174 Handler h = wh.get();
175 if (h != null) {
176 h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED,
177 orientation, 0));
178 } else {
179 handlersToRemove.add(wh);
180 }
181 }
182 // Now remove all the null references.
183 for (WeakReference weak : handlersToRemove) {
184 mHandlers.remove(weak);
185 }
186 }
187 }
188
189 public void onLowMemory() {}
190 }
191 static ConfigCallback sConfigCallback;
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 /**
194 * Create a new BrowserFrame to be used in an application.
195 * @param context An application context to use when retrieving assets.
196 * @param w A WebViewCore used as the view for this frame.
197 * @param proxy A CallbackProxy for posting messages to the UI thread and
198 * querying a client for information.
199 * @param settings A WebSettings object that holds all settings.
200 * XXX: Called by WebCore thread.
201 */
202 public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100203 WebSettings settings, Map<String, Object> javascriptInterfaces) {
Romain Guy01d0fbf2009-12-01 14:52:19 -0800204
205 Context appContext = context.getApplicationContext();
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 // Create a global JWebCoreJavaBridge to handle timers and
208 // cookies in the WebCore thread.
209 if (sJavaBridge == null) {
Grace Kloba9b95ab12010-04-01 16:07:30 -0700210 sJavaBridge = new JWebCoreJavaBridge();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 // set WebCore native cache size
Grace Kloba11843132009-10-08 20:48:09 -0700212 ActivityManager am = (ActivityManager) context
213 .getSystemService(Context.ACTIVITY_SERVICE);
214 if (am.getMemoryClass() > 16) {
215 sJavaBridge.setCacheSize(8 * 1024 * 1024);
216 } else {
217 sJavaBridge.setCacheSize(4 * 1024 * 1024);
218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 // initialize CacheManager
Romain Guy01d0fbf2009-12-01 14:52:19 -0800220 CacheManager.init(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 // create CookieSyncManager with current Context
Romain Guy01d0fbf2009-12-01 14:52:19 -0800222 CookieSyncManager.createInstance(appContext);
Grace Kloba658ab7d2009-05-14 14:45:26 -0700223 // create PluginManager with current Context
Romain Guy01d0fbf2009-12-01 14:52:19 -0800224 PluginManager.getInstance(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
Patrick Scott11f57382010-03-25 11:18:59 -0400226
227 if (sConfigCallback == null) {
228 sConfigCallback = new ConfigCallback(
Patrick Scott624051a2011-01-05 13:36:00 -0500229 (WindowManager) appContext.getSystemService(
Patrick Scott11f57382010-03-25 11:18:59 -0400230 Context.WINDOW_SERVICE));
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700231 ViewRootImpl.addConfigCallback(sConfigCallback);
Patrick Scott11f57382010-03-25 11:18:59 -0400232 }
233 sConfigCallback.addHandler(this);
234
Steve Block2cb978e2011-07-01 17:55:51 +0100235 mJavaScriptObjects = javascriptInterfaces;
236 if (mJavaScriptObjects == null) {
237 mJavaScriptObjects = new HashMap<String, Object>();
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000238 }
Steve Block2cb978e2011-07-01 17:55:51 +0100239 mRemovedJavaScriptObjects = new HashSet<Object>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240
241 mSettings = settings;
242 mContext = context;
243 mCallbackProxy = proxy;
Romain Guy01d0fbf2009-12-01 14:52:19 -0800244 mDatabase = WebViewDatabase.getInstance(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 mWebViewCore = w;
246
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000247 mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
Steve Block2cb978e2011-07-01 17:55:51 +0100248 mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000249
Grace Kloba83031582009-09-02 16:21:42 -0700250 AssetManager am = context.getAssets();
251 nativeCreateFrame(w, am, proxy.getBackForwardList());
252
Derek Sollenberger2e5c1502009-06-03 10:44:42 -0400253 if (DebugFlags.BROWSER_FRAME) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
255 }
256 }
257
258 /**
259 * Load a url from the network or the filesystem into the main frame.
Grace Klobad0d9bc22010-01-26 18:08:28 -0800260 * Following the same behaviour as Safari, javascript: URLs are not passed
261 * to the main frame, instead they are evaluated immediately.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * @param url The url to load.
Grace Klobad0d9bc22010-01-26 18:08:28 -0800263 * @param extraHeaders The extra headers sent with this url. This should not
264 * include the common headers like "user-agent". If it does, it
265 * will be replaced by the intrinsic value of the WebView.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 */
Grace Klobad0d9bc22010-01-26 18:08:28 -0800267 public void loadUrl(String url, Map<String, String> extraHeaders) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 mLoadInitFromJava = true;
269 if (URLUtil.isJavaScriptUrl(url)) {
270 // strip off the scheme and evaluate the string
271 stringByEvaluatingJavaScriptFromString(
272 url.substring("javascript:".length()));
273 } else {
Grace Klobad0d9bc22010-01-26 18:08:28 -0800274 nativeLoadUrl(url, extraHeaders);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276 mLoadInitFromJava = false;
277 }
278
279 /**
Grace Kloba57534302009-05-22 18:55:02 -0700280 * Load a url with "POST" method from the network into the main frame.
281 * @param url The url to load.
282 * @param data The data for POST request.
283 */
284 public void postUrl(String url, byte[] data) {
285 mLoadInitFromJava = true;
286 nativePostUrl(url, data);
287 mLoadInitFromJava = false;
288 }
289
290 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 * Load the content as if it was loaded by the provided base URL. The
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400292 * historyUrl is used as the history entry for the load data.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 *
294 * @param baseUrl Base URL used to resolve relative paths in the content
295 * @param data Content to render in the browser
296 * @param mimeType Mimetype of the data being passed in
297 * @param encoding Character set encoding of the provided data.
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400298 * @param historyUrl URL to use as the history entry.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 */
300 public void loadData(String baseUrl, String data, String mimeType,
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400301 String encoding, String historyUrl) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 mLoadInitFromJava = true;
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400303 if (historyUrl == null || historyUrl.length() == 0) {
304 historyUrl = "about:blank";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
306 if (data == null) {
307 data = "";
308 }
309
310 // Setup defaults for missing values. These defaults where taken from
311 // WebKit's WebFrame.mm
312 if (baseUrl == null || baseUrl.length() == 0) {
313 baseUrl = "about:blank";
314 }
315 if (mimeType == null || mimeType.length() == 0) {
316 mimeType = "text/html";
317 }
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400318 nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 mLoadInitFromJava = false;
320 }
321
322 /**
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -0700323 * Saves the contents of the frame as a web archive.
324 *
325 * @param basename The filename where the archive should be placed.
326 * @param autoname If false, takes filename to be a file. If true, filename
327 * is assumed to be a directory in which a filename will be
328 * chosen according to the url of the current page.
329 */
330 /* package */ String saveWebArchive(String basename, boolean autoname) {
331 return nativeSaveWebArchive(basename, autoname);
332 }
333
334 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 * Go back or forward the number of steps given.
336 * @param steps A negative or positive number indicating the direction
337 * and number of steps to move.
338 */
339 public void goBackOrForward(int steps) {
340 mLoadInitFromJava = true;
341 nativeGoBackOrForward(steps);
342 mLoadInitFromJava = false;
343 }
344
345 /**
346 * native callback
347 * Report an error to an activity.
348 * @param errorCode The HTTP error code.
Iain Merrick83d4a232010-11-12 14:11:16 +0000349 * @param description Optional human-readable description. If no description
350 * is given, we'll use a standard localized error message.
351 * @param failingUrl The URL that was being loaded when the error occurred.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 * TODO: Report all errors including resource errors but include some kind
353 * of domain identifier. Change errorCode to an enum for a cleaner
354 * interface.
355 */
Iain Merrick83d4a232010-11-12 14:11:16 +0000356 private void reportError(int errorCode, String description, String failingUrl) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 // As this is called for the main resource and loading will be stopped
358 // after, reset the state variables.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800359 resetLoadingStates();
Iain Merrick83d4a232010-11-12 14:11:16 +0000360 if (description == null || description.isEmpty()) {
361 description = ErrorStrings.getString(errorCode, mContext);
362 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800363 mCallbackProxy.onReceivedError(errorCode, description, failingUrl);
364 }
365
366 private void resetLoadingStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 mCommitted = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 mFirstLayoutDone = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 }
370
371 /* package */boolean committed() {
372 return mCommitted;
373 }
374
375 /* package */boolean firstLayoutDone() {
376 return mFirstLayoutDone;
377 }
378
379 /* package */int loadType() {
380 return mLoadType;
381 }
382
383 /* package */void didFirstLayout() {
384 if (!mFirstLayoutDone) {
385 mFirstLayoutDone = true;
386 // ensure {@link WebViewCore#webkitDraw} is called as we were
387 // blocking the update in {@link #loadStarted}
388 mWebViewCore.contentDraw();
389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 }
391
392 /**
393 * native callback
394 * Indicates the beginning of a new load.
395 * This method will be called once for the main frame.
396 */
397 private void loadStarted(String url, Bitmap favicon, int loadType,
398 boolean isMainFrame) {
399 mIsMainFrame = isMainFrame;
400
401 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
402 mLoadType = loadType;
403
404 if (isMainFrame) {
405 // Call onPageStarted for main frames.
406 mCallbackProxy.onPageStarted(url, favicon);
407 // as didFirstLayout() is only called for the main frame, reset
408 // mFirstLayoutDone only for the main frames
409 mFirstLayoutDone = false;
410 mCommitted = false;
411 // remove pending draw to block update until mFirstLayoutDone is
412 // set to true in didFirstLayout()
Ben Murdocha62b9632011-12-05 14:51:00 +0000413 mWebViewCore.clearContent();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW);
415 }
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500416 }
417 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500419 @SuppressWarnings("unused")
420 private void saveFormData(HashMap<String, String> data) {
421 if (mSettings.getSaveFormData()) {
422 final WebHistoryItem h = mCallbackProxy.getBackForwardList()
423 .getCurrentItem();
424 if (h != null) {
Ben Murdoch2376f822011-09-09 16:49:44 +0100425 String url = WebTextView.urlForAutoCompleteData(h.getUrl());
426 if (url != null) {
427 mDatabase.setFormData(url, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429 }
430 }
431 }
432
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500433 @SuppressWarnings("unused")
434 private boolean shouldSaveFormData() {
435 if (mSettings.getSaveFormData()) {
436 final WebHistoryItem h = mCallbackProxy.getBackForwardList()
437 .getCurrentItem();
438 return h != null && h.getUrl() != null;
439 }
440 return false;
441 }
442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 /**
444 * native callback
445 * Indicates the WebKit has committed to the new load
446 */
447 private void transitionToCommitted(int loadType, boolean isMainFrame) {
448 // loadType is not used yet
449 if (isMainFrame) {
450 mCommitted = true;
Grace Kloba9a67c822009-12-20 11:33:58 -0800451 mWebViewCore.getWebView().mViewManager.postResetStateAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 }
453 }
454
455 /**
456 * native callback
457 * <p>
458 * Indicates the end of a new load.
459 * This method will be called once for the main frame.
460 */
461 private void loadFinished(String url, int loadType, boolean isMainFrame) {
462 // mIsMainFrame and isMainFrame are better be equal!!!
463
464 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
465 if (isMainFrame) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800466 resetLoadingStates();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 mCallbackProxy.switchOutDrawHistory();
468 mCallbackProxy.onPageFinished(url);
469 }
470 }
471 }
472
473 /**
474 * We have received an SSL certificate for the main top-level page.
Steve Blockfe33a752011-10-04 19:09:13 +0100475 * Used by the Android HTTP stack only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 */
477 void certificate(SslCertificate certificate) {
478 if (mIsMainFrame) {
479 // we want to make this call even if the certificate is null
480 // (ie, the site is not secure)
481 mCallbackProxy.onReceivedCertificate(certificate);
482 }
483 }
484
485 /**
486 * Destroy all native components of the BrowserFrame.
487 */
488 public void destroy() {
489 nativeDestroyFrame();
Leon Clarkec0b778e2010-03-15 15:27:02 +0000490 mBlockMessages = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 removeCallbacksAndMessages(null);
492 }
493
494 /**
495 * Handle messages posted to us.
496 * @param msg The message to handle.
497 */
498 @Override
499 public void handleMessage(Message msg) {
Leon Clarkec0b778e2010-03-15 15:27:02 +0000500 if (mBlockMessages) {
501 return;
502 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 switch (msg.what) {
504 case FRAME_COMPLETED: {
505 if (mSettings.getSavePassword() && hasPasswordField()) {
Patrick Scottaf31c3a2009-08-24 13:46:09 -0400506 WebHistoryItem item = mCallbackProxy.getBackForwardList()
507 .getCurrentItem();
508 if (item != null) {
509 WebAddress uri = new WebAddress(item.getUrl());
Bjorn Bringerteb8be972010-10-12 16:24:55 +0100510 String schemePlusHost = uri.getScheme() + uri.getHost();
Patrick Scottaf31c3a2009-08-24 13:46:09 -0400511 String[] up =
512 mDatabase.getUsernamePassword(schemePlusHost);
513 if (up != null && up[0] != null) {
514 setUsernamePassword(up[0], up[1]);
515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
517 }
Steve Block808751f2011-01-04 14:26:27 +0000518 if (!JniUtil.useChromiumHttpStack()) {
519 WebViewWorker.getHandler().sendEmptyMessage(
520 WebViewWorker.MSG_TRIM_CACHE);
521 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 break;
523 }
524
525 case POLICY_FUNCTION: {
526 nativeCallPolicyFunction(msg.arg1, msg.arg2);
527 break;
528 }
529
Patrick Scottf06364b2009-12-02 08:57:09 -0500530 case ORIENTATION_CHANGED: {
Shimeng (Simon) Wang5f7c5a22010-10-11 13:12:48 -0700531 if (mOrientation != msg.arg1) {
532 mOrientation = msg.arg1;
533 nativeOrientationChanged(msg.arg1);
534 }
Patrick Scottf06364b2009-12-02 08:57:09 -0500535 break;
536 }
537
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 default:
539 break;
540 }
541 }
542
543 /**
544 * Punch-through for WebCore to set the document
545 * title. Inform the Activity of the new title.
546 * @param title The new title of the document.
547 */
548 private void setTitle(String title) {
549 // FIXME: The activity must call getTitle (a native method) to get the
550 // title. We should try and cache the title if we can also keep it in
551 // sync with the document.
552 mCallbackProxy.onReceivedTitle(title);
553 }
554
555 /**
556 * Retrieves the render tree of this frame and puts it as the object for
557 * the message and sends the message.
558 * @param callback the message to use to send the render tree
559 */
560 public void externalRepresentation(Message callback) {
561 callback.obj = externalRepresentation();;
562 callback.sendToTarget();
563 }
564
565 /**
566 * Return the render tree as a string
567 */
568 private native String externalRepresentation();
569
570 /**
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700571 * Retrieves the visual text of the frames, puts it as the object for
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 * the message and sends the message.
573 * @param callback the message to use to send the visual text
574 */
575 public void documentAsText(Message callback) {
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700576 StringBuilder text = new StringBuilder();
577 if (callback.arg1 != 0) {
578 // Dump top frame as text.
579 text.append(documentAsText());
580 }
581 if (callback.arg2 != 0) {
582 // Dump child frames as text.
583 text.append(childFramesAsText());
584 }
585 callback.obj = text.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 callback.sendToTarget();
587 }
588
589 /**
590 * Return the text drawn on the screen as a string
591 */
592 private native String documentAsText();
593
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700594 /**
595 * Return the text drawn on the child frames as a string
596 */
597 private native String childFramesAsText();
598
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 /*
600 * This method is called by WebCore to inform the frame that
601 * the Javascript window object has been cleared.
602 * We should re-attach any attached js interfaces.
603 */
604 private void windowObjectCleared(int nativeFramePointer) {
Steve Block2cb978e2011-07-01 17:55:51 +0100605 Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000606 while (iter.hasNext()) {
607 String interfaceName = iter.next();
Steve Block2cb978e2011-07-01 17:55:51 +0100608 Object object = mJavaScriptObjects.get(interfaceName);
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000609 if (object != null) {
610 nativeAddJavascriptInterface(nativeFramePointer,
Steve Block2cb978e2011-07-01 17:55:51 +0100611 mJavaScriptObjects.get(interfaceName), interfaceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 }
613 }
Steve Block2cb978e2011-07-01 17:55:51 +0100614 mRemovedJavaScriptObjects.clear();
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000615
616 stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
618
619 /**
620 * This method is called by WebCore to check whether application
621 * wants to hijack url loading
622 */
623 public boolean handleUrl(String url) {
624 if (mLoadInitFromJava == true) {
625 return false;
626 }
627 if (mCallbackProxy.shouldOverrideUrlLoading(url)) {
628 // if the url is hijacked, reset the state of the BrowserFrame
629 didFirstLayout();
630 return true;
631 } else {
632 return false;
633 }
634 }
635
636 public void addJavascriptInterface(Object obj, String interfaceName) {
Steve Block544295e2010-12-02 18:40:06 +0000637 assert obj != null;
Steve Block689a3422010-12-07 18:18:26 +0000638 removeJavascriptInterface(interfaceName);
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000639
Steve Block2cb978e2011-07-01 17:55:51 +0100640 mJavaScriptObjects.put(interfaceName, obj);
Steve Block689a3422010-12-07 18:18:26 +0000641 }
642
643 public void removeJavascriptInterface(String interfaceName) {
Steve Block2cb978e2011-07-01 17:55:51 +0100644 // We keep a reference to the removed object because the native side holds only a weak
645 // reference and we need to allow the object to continue to be used until the page has been
646 // navigated.
647 if (mJavaScriptObjects.containsKey(interfaceName)) {
648 mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 }
651
652 /**
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400653 * Called by JNI. Given a URI, find the associated file and return its size
654 * @param uri A String representing the URI of the desired file.
655 * @return int The size of the given file.
656 */
657 private int getFileSize(String uri) {
658 int size = 0;
Cary Clarkac96fa52010-03-22 10:56:37 -0400659 try {
660 InputStream stream = mContext.getContentResolver()
661 .openInputStream(Uri.parse(uri));
662 size = stream.available();
663 stream.close();
664 } catch (Exception e) {}
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400665 return size;
666 }
667
668 /**
669 * Called by JNI. Given a URI, a buffer, and an offset into the buffer,
670 * copy the resource into buffer.
671 * @param uri A String representing the URI of the desired file.
672 * @param buffer The byte array to copy the data into.
673 * @param offset The offet into buffer to place the data.
Romain Guy01d0fbf2009-12-01 14:52:19 -0800674 * @param expectedSize The size that the buffer has allocated for this file.
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400675 * @return int The size of the given file, or zero if it fails.
676 */
Leon Scrogginsd5ba82a2009-10-13 14:23:56 -0400677 private int getFile(String uri, byte[] buffer, int offset,
678 int expectedSize) {
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400679 int size = 0;
680 try {
681 InputStream stream = mContext.getContentResolver()
682 .openInputStream(Uri.parse(uri));
683 size = stream.available();
Leon Scrogginsd5ba82a2009-10-13 14:23:56 -0400684 if (size <= expectedSize && buffer != null
685 && buffer.length - offset >= size) {
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400686 stream.read(buffer, offset, size);
687 } else {
688 size = 0;
689 }
690 stream.close();
691 } catch (java.io.FileNotFoundException e) {
692 Log.e(LOGTAG, "FileNotFoundException:" + e);
693 size = 0;
694 } catch (java.io.IOException e2) {
695 Log.e(LOGTAG, "IOException: " + e2);
696 size = 0;
697 }
698 return size;
699 }
700
701 /**
Kristian Monsendba35c02010-08-19 20:22:53 +0100702 * Get the InputStream for an Android resource
703 * There are three different kinds of android resources:
704 * - file:///android_res
705 * - file:///android_asset
706 * - content://
707 * @param url The url to load.
708 * @return An InputStream to the android resource
709 */
Kristian Monsenbadf9452010-11-01 15:49:45 +0000710 private InputStream inputStreamForAndroidResource(String url) {
711 // This list needs to be kept in sync with the list in
712 // external/webkit/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
713 final String ANDROID_ASSET = "file:///android_asset/";
714 final String ANDROID_RESOURCE = "file:///android_res/";
715 final String ANDROID_CONTENT = "content:";
Kristian Monsendba35c02010-08-19 20:22:53 +0100716
Kristian Monsenbadf9452010-11-01 15:49:45 +0000717 // file:///android_res
718 if (url.startsWith(ANDROID_RESOURCE)) {
719 url = url.replaceFirst(ANDROID_RESOURCE, "");
Kristian Monsendba35c02010-08-19 20:22:53 +0100720 if (url == null || url.length() == 0) {
721 Log.e(LOGTAG, "url has length 0 " + url);
722 return null;
723 }
724 int slash = url.indexOf('/');
725 int dot = url.indexOf('.', slash);
726 if (slash == -1 || dot == -1) {
727 Log.e(LOGTAG, "Incorrect res path: " + url);
728 return null;
729 }
730 String subClassName = url.substring(0, slash);
731 String fieldName = url.substring(slash + 1, dot);
732 String errorMsg = null;
733 try {
734 final Class<?> d = mContext.getApplicationContext()
735 .getClassLoader().loadClass(
736 mContext.getPackageName() + ".R$"
737 + subClassName);
738 final java.lang.reflect.Field field = d.getField(fieldName);
739 final int id = field.getInt(null);
740 TypedValue value = new TypedValue();
741 mContext.getResources().getValue(id, value, true);
742 if (value.type == TypedValue.TYPE_STRING) {
743 return mContext.getAssets().openNonAsset(
744 value.assetCookie, value.string.toString(),
745 AssetManager.ACCESS_STREAMING);
746 } else {
747 // Old stack only supports TYPE_STRING for res files
748 Log.e(LOGTAG, "not of type string: " + url);
749 return null;
750 }
751 } catch (Exception e) {
752 Log.e(LOGTAG, "Exception: " + url);
753 return null;
754 }
755
Kristian Monsenbadf9452010-11-01 15:49:45 +0000756 // file:///android_asset
757 } else if (url.startsWith(ANDROID_ASSET)) {
758 url = url.replaceFirst(ANDROID_ASSET, "");
Kristian Monsendba35c02010-08-19 20:22:53 +0100759 try {
760 AssetManager assets = mContext.getAssets();
761 return assets.open(url, AssetManager.ACCESS_STREAMING);
762 } catch (IOException e) {
763 return null;
764 }
Kristian Monsenbadf9452010-11-01 15:49:45 +0000765
766 // content://
Patrick Scottd1737ed2011-01-05 11:36:48 -0500767 } else if (mSettings.getAllowContentAccess() &&
768 url.startsWith(ANDROID_CONTENT)) {
Kristian Monsendba35c02010-08-19 20:22:53 +0100769 try {
Iain Merrick2bcd2922010-10-27 12:33:09 +0100770 // Strip off mimetype, for compatibility with ContentLoader.java
771 // If we don't do this, we can fail to load Gmail attachments,
772 // because the URL being loaded doesn't exactly match the URL we
773 // have permission to read.
774 int mimeIndex = url.lastIndexOf('?');
775 if (mimeIndex != -1) {
776 url = url.substring(0, mimeIndex);
777 }
Kristian Monsendba35c02010-08-19 20:22:53 +0100778 Uri uri = Uri.parse(url);
779 return mContext.getContentResolver().openInputStream(uri);
780 } catch (Exception e) {
781 Log.e(LOGTAG, "Exception: " + url);
782 return null;
783 }
784 } else {
785 return null;
786 }
787 }
788
789 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 * Start loading a resource.
791 * @param loaderHandle The native ResourceLoader that is the target of the
792 * data.
793 * @param url The url to load.
794 * @param method The http method.
795 * @param headers The http headers.
796 * @param postData If the method is "POST" postData is sent as the request
797 * body. Is null when empty.
Brian Carlstromdba8cb72010-03-18 16:56:41 -0700798 * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
799 * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
800 * @param mainResource True if the this resource is the main request, not a supporting resource
801 * @param userGesture
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 * @param synchronous True if the load is synchronous.
803 * @return A newly created LoadListener object.
804 */
805 private LoadListener startLoadingResource(int loaderHandle,
806 String url,
807 String method,
808 HashMap headers,
809 byte[] postData,
Grace Kloba8c92c392009-11-08 19:01:55 -0800810 long postDataIdentifier,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 int cacheMode,
Patrick Scott7c24ed62009-11-30 13:34:40 -0500812 boolean mainResource,
813 boolean userGesture,
Steve Blockea08c512010-03-24 16:24:25 +0000814 boolean synchronous,
815 String username,
816 String password) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) {
818 cacheMode = mSettings.getCacheMode();
819 }
820
821 if (method.equals("POST")) {
822 // Don't use the cache on POSTs when issuing a normal POST
823 // request.
824 if (cacheMode == WebSettings.LOAD_NORMAL) {
825 cacheMode = WebSettings.LOAD_NO_CACHE;
826 }
Iain Merrick9eb2a892010-11-18 15:32:31 +0000827 String[] ret = getUsernamePassword();
828 if (ret != null) {
829 String domUsername = ret[0];
830 String domPassword = ret[1];
831 maybeSavePassword(postData, domUsername, domPassword);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
833 }
834
835 // is this resource the main-frame top-level page?
836 boolean isMainFramePage = mIsMainFrame;
837
Derek Sollenberger2e5c1502009-06-03 10:44:42 -0400838 if (DebugFlags.BROWSER_FRAME) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
Patrick Scottfe4fec72009-07-14 15:54:30 -0400840 + method + ", postData=" + postData + ", isMainFramePage="
Patrick Scott7c24ed62009-11-30 13:34:40 -0500841 + isMainFramePage + ", mainResource=" + mainResource
842 + ", userGesture=" + userGesture);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844
845 // Create a LoadListener
Grace Kloba8c92c392009-11-08 19:01:55 -0800846 LoadListener loadListener = LoadListener.getLoadListener(mContext,
847 this, url, loaderHandle, synchronous, isMainFramePage,
Steve Blockea08c512010-03-24 16:24:25 +0000848 mainResource, userGesture, postDataIdentifier, username, password);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) {
Grace Klobafadbbd22009-07-17 20:39:25 -0700851 // send an error message, so that loadListener can be deleted
852 // after this is returned. This is important as LoadListener's
853 // nativeError will remove the request from its DocLoader's request
854 // list. But the set up is not done until this method is returned.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 loadListener.error(
856 android.net.http.EventHandler.ERROR, mContext.getString(
857 com.android.internal.R.string.httpErrorTooManyRequests));
Grace Klobafadbbd22009-07-17 20:39:25 -0700858 return loadListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
860
Patrick Scottc12544a2010-11-11 13:16:44 -0500861 // Note that we are intentionally skipping
862 // inputStreamForAndroidResource. This is so that FrameLoader will use
863 // the various StreamLoader classes to handle assets.
864 FrameLoader loader = new FrameLoader(loadListener, mSettings, method,
865 mCallbackProxy.shouldInterceptRequest(url));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 loader.setHeaders(headers);
867 loader.setPostData(postData);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800868 // Set the load mode to the mode used for the current page.
869 // If WebKit wants validation, go to network directly.
870 loader.setCacheMode(headers.containsKey("If-Modified-Since")
871 || headers.containsKey("If-None-Match") ?
872 WebSettings.LOAD_NO_CACHE : cacheMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 // Set referrer to current URL?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 return !synchronous ? loadListener : null;
875 }
876
Iain Merrick9eb2a892010-11-18 15:32:31 +0000877 /**
878 * If this looks like a POST request (form submission) containing a username
879 * and password, give the user the option of saving them. Will either do
880 * nothing, or block until the UI interaction is complete.
881 *
882 * Called by startLoadingResource when using the Apache HTTP stack.
883 * Called directly by WebKit when using the Chrome HTTP stack.
884 *
885 * @param postData The data about to be sent as the body of a POST request.
886 * @param username The username entered by the user (sniffed from the DOM).
887 * @param password The password entered by the user (sniffed from the DOM).
888 */
889 private void maybeSavePassword(
890 byte[] postData, String username, String password) {
891 if (postData == null
892 || username == null || username.isEmpty()
893 || password == null || password.isEmpty()) {
894 return; // No password to save.
895 }
896
897 if (!mSettings.getSavePassword()) {
898 return; // User doesn't want to save passwords.
899 }
900
901 try {
902 if (DebugFlags.BROWSER_FRAME) {
903 Assert.assertNotNull(mCallbackProxy.getBackForwardList()
904 .getCurrentItem());
905 }
906 WebAddress uri = new WebAddress(mCallbackProxy
907 .getBackForwardList().getCurrentItem().getUrl());
908 String schemePlusHost = uri.getScheme() + uri.getHost();
909 // Check to see if the username & password appear in
910 // the post data (there could be another form on the
911 // page and that was posted instead.
912 String postString = new String(postData);
913 if (postString.contains(URLEncoder.encode(username)) &&
914 postString.contains(URLEncoder.encode(password))) {
915 String[] saved = mDatabase.getUsernamePassword(
916 schemePlusHost);
917 if (saved != null) {
918 // null username implies that user has chosen not to
919 // save password
920 if (saved[0] != null) {
921 // non-null username implies that user has
922 // chosen to save password, so update the
923 // recorded password
924 mDatabase.setUsernamePassword(
925 schemePlusHost, username, password);
926 }
927 } else {
928 // CallbackProxy will handle creating the resume
929 // message
930 mCallbackProxy.onSavePassword(schemePlusHost, username,
931 password, null);
932 }
933 }
934 } catch (ParseException ex) {
935 // if it is bad uri, don't save its password
936 }
937 }
938
Patrick Scottc12544a2010-11-11 13:16:44 -0500939 // Called by jni from the chrome network stack.
940 private WebResourceResponse shouldInterceptRequest(String url) {
941 InputStream androidResource = inputStreamForAndroidResource(url);
942 if (androidResource != null) {
943 return new WebResourceResponse(null, null, androidResource);
944 }
John Reckd6662b52011-02-16 14:21:21 -0800945 WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url);
946 if (response == null && "browser:incognito".equals(url)) {
947 try {
948 Resources res = mContext.getResources();
949 InputStream ins = res.openRawResource(
950 com.android.internal.R.raw.incognito_mode_start_page);
951 response = new WebResourceResponse("text/html", "utf8", ins);
952 } catch (NotFoundException ex) {
953 // This shouldn't happen, but try and gracefully handle it jic
954 Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex);
955 }
956 }
957 return response;
Patrick Scottc12544a2010-11-11 13:16:44 -0500958 }
959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 /**
961 * Set the progress for the browser activity. Called by native code.
962 * Uses a delay so it does not happen too often.
963 * @param newProgress An int between zero and one hundred representing
964 * the current progress percentage of loading the page.
965 */
966 private void setProgress(int newProgress) {
967 mCallbackProxy.onProgressChanged(newProgress);
968 if (newProgress == 100) {
969 sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100);
970 }
971 // FIXME: Need to figure out a better way to switch out of the history
972 // drawing mode. Maybe we can somehow compare the history picture with
973 // the current picture, and switch when it contains more content.
974 if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) {
975 mCallbackProxy.switchOutDrawHistory();
976 }
977 }
978
979 /**
980 * Send the icon to the activity for display.
981 * @param icon A Bitmap representing a page's favicon.
982 */
983 private void didReceiveIcon(Bitmap icon) {
984 mCallbackProxy.onReceivedIcon(icon);
985 }
986
Patrick Scott2ba12622009-08-04 13:20:05 -0400987 // Called by JNI when an apple-touch-icon attribute was found.
Patrick Scottd58ccff2009-09-18 16:29:00 -0400988 private void didReceiveTouchIconUrl(String url, boolean precomposed) {
989 mCallbackProxy.onReceivedTouchIconUrl(url, precomposed);
Patrick Scott2ba12622009-08-04 13:20:05 -0400990 }
991
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 /**
993 * Request a new window from the client.
994 * @return The BrowserFrame object stored in the new WebView.
995 */
996 private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
Patrick Scott97147282010-06-16 12:27:06 -0400997 return mCallbackProxy.createWindow(dialog, userGesture);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 }
999
1000 /**
1001 * Try to focus this WebView.
1002 */
1003 private void requestFocus() {
1004 mCallbackProxy.onRequestFocus();
1005 }
1006
1007 /**
1008 * Close this frame and window.
1009 */
1010 private void closeWindow(WebViewCore w) {
1011 mCallbackProxy.onCloseWindow(w.getWebView());
1012 }
1013
1014 // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore
1015 static final int POLICY_USE = 0;
1016 static final int POLICY_IGNORE = 2;
1017
1018 private void decidePolicyForFormResubmission(int policyFunction) {
1019 Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction,
1020 POLICY_IGNORE);
1021 Message resend = obtainMessage(POLICY_FUNCTION, policyFunction,
1022 POLICY_USE);
1023 mCallbackProxy.onFormResubmission(dontResend, resend);
1024 }
1025
1026 /**
1027 * Tell the activity to update its global history.
1028 */
1029 private void updateVisitedHistory(String url, boolean isReload) {
1030 mCallbackProxy.doUpdateVisitedHistory(url, isReload);
1031 }
1032
1033 /**
1034 * Get the CallbackProxy for sending messages to the UI thread.
1035 */
1036 /* package */ CallbackProxy getCallbackProxy() {
1037 return mCallbackProxy;
1038 }
1039
1040 /**
1041 * Returns the User Agent used by this frame
1042 */
1043 String getUserAgentString() {
1044 return mSettings.getUserAgentString();
1045 }
1046
Leon Scrogginsaacced62009-12-11 15:01:22 -05001047 // These ids need to be in sync with enum rawResId in PlatformBridge.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 private static final int NODOMAIN = 1;
1049 private static final int LOADERROR = 2;
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001050 /* package */ static final int DRAWABLEDIR = 3;
Leon Scrogginsaacced62009-12-11 15:01:22 -05001051 private static final int FILE_UPLOAD_LABEL = 4;
1052 private static final int RESET_LABEL = 5;
1053 private static final int SUBMIT_LABEL = 6;
Ben Murdochfa296a42010-05-18 16:16:40 +01001054 private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001056 private String getRawResFilename(int id) {
1057 return getRawResFilename(id, mContext);
1058 }
1059 /* package */ static String getRawResFilename(int id, Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 int resid;
1061 switch (id) {
1062 case NODOMAIN:
1063 resid = com.android.internal.R.raw.nodomain;
1064 break;
1065
1066 case LOADERROR:
1067 resid = com.android.internal.R.raw.loaderror;
1068 break;
1069
Grace Kloba83031582009-09-02 16:21:42 -07001070 case DRAWABLEDIR:
1071 // use one known resource to find the drawable directory
1072 resid = com.android.internal.R.drawable.btn_check_off;
1073 break;
1074
Leon Scrogginsaacced62009-12-11 15:01:22 -05001075 case FILE_UPLOAD_LABEL:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001076 return context.getResources().getString(
Leon Scrogginsaacced62009-12-11 15:01:22 -05001077 com.android.internal.R.string.upload_file);
1078
1079 case RESET_LABEL:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001080 return context.getResources().getString(
Leon Scrogginsaacced62009-12-11 15:01:22 -05001081 com.android.internal.R.string.reset);
1082
1083 case SUBMIT_LABEL:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001084 return context.getResources().getString(
Leon Scrogginsaacced62009-12-11 15:01:22 -05001085 com.android.internal.R.string.submit);
1086
Ben Murdochfa296a42010-05-18 16:16:40 +01001087 case FILE_UPLOAD_NO_FILE_CHOSEN:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001088 return context.getResources().getString(
Ben Murdochfa296a42010-05-18 16:16:40 +01001089 com.android.internal.R.string.no_file_chosen);
1090
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 default:
1092 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
Cary Clark686cf752009-08-11 16:08:52 -04001093 return "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 }
1095 TypedValue value = new TypedValue();
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001096 context.getResources().getValue(resid, value, true);
Grace Kloba83031582009-09-02 16:21:42 -07001097 if (id == DRAWABLEDIR) {
1098 String path = value.string.toString();
1099 int index = path.lastIndexOf('/');
1100 if (index < 0) {
1101 Log.e(LOGTAG, "Can't find drawable directory.");
1102 return "";
1103 }
1104 return path.substring(0, index + 1);
1105 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 return value.string.toString();
1107 }
1108
Grace Kloba408cf852009-09-20 16:34:44 -07001109 private float density() {
1110 return mContext.getResources().getDisplayMetrics().density;
1111 }
1112
Iain Merrick10229b22010-08-31 11:57:15 +01001113 /**
1114 * Called by JNI when the native HTTP stack gets an authentication request.
1115 *
1116 * We delegate the request to CallbackProxy, and route its response to
1117 * {@link #nativeAuthenticationProceed(int, String, String)} or
1118 * {@link #nativeAuthenticationCancel(int)}.
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001119 *
1120 * We don't care what thread the callback is invoked on. All threading is
1121 * handled on the C++ side, because the WebKit thread may be blocked on a
1122 * synchronous call and unable to pump our MessageQueue.
Iain Merrick10229b22010-08-31 11:57:15 +01001123 */
1124 private void didReceiveAuthenticationChallenge(
Ben Murdocha7f0ed42011-08-05 15:46:52 +01001125 final int handle, String host, String realm, final boolean useCachedCredentials,
1126 final boolean suppressDialog) {
Iain Merrick10229b22010-08-31 11:57:15 +01001127
1128 HttpAuthHandler handler = new HttpAuthHandler() {
1129
Iain Merrick10229b22010-08-31 11:57:15 +01001130 @Override
1131 public boolean useHttpAuthUsernamePassword() {
1132 return useCachedCredentials;
1133 }
1134
1135 @Override
1136 public void proceed(String username, String password) {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001137 nativeAuthenticationProceed(handle, username, password);
Iain Merrick10229b22010-08-31 11:57:15 +01001138 }
1139
1140 @Override
1141 public void cancel() {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001142 nativeAuthenticationCancel(handle);
Iain Merrick10229b22010-08-31 11:57:15 +01001143 }
Ben Murdocha7f0ed42011-08-05 15:46:52 +01001144
1145 @Override
1146 public boolean suppressDialog() {
1147 return suppressDialog;
1148 }
Iain Merrick10229b22010-08-31 11:57:15 +01001149 };
1150 mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
1151 }
1152
Iain Merrickd77f9b72010-10-18 18:22:25 +01001153 /**
Steve Block200ff0a2011-09-27 12:51:18 +01001154 * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain.
Huahui Wuad053ce2010-12-08 15:24:55 -08001155 *
1156 * We delegate the request to CallbackProxy, and route its response to
1157 * {@link #nativeSslCertErrorProceed(int)} or
1158 * {@link #nativeSslCertErrorCancel(int, int)}.
1159 */
Steve Block200ff0a2011-09-27 12:51:18 +01001160 private void reportSslCertError(final int handle, final int certError, byte certDER[],
1161 String url) {
1162 final SslError sslError;
Huahui Wuad053ce2010-12-08 15:24:55 -08001163 try {
Steve Block200ff0a2011-09-27 12:51:18 +01001164 X509Certificate cert = new X509CertImpl(certDER);
Kristian Monsen0d6c6a72011-07-13 20:24:39 +01001165 SslCertificate sslCert = new SslCertificate(cert);
Steve Blockfa03f9a2011-10-05 12:45:37 +01001166 sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url);
Huahui Wuad053ce2010-12-08 15:24:55 -08001167 } catch (IOException e) {
Huahui Wuc56c3e22011-02-10 14:40:50 -08001168 // Can't get the certificate, not much to do.
1169 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
Steve Block200ff0a2011-09-27 12:51:18 +01001170 nativeSslCertErrorCancel(handle, certError);
1171 return;
1172 }
1173
1174 if (SslCertLookupTable.getInstance().isAllowed(sslError)) {
1175 nativeSslCertErrorProceed(handle);
Steve Blockfe33a752011-10-04 19:09:13 +01001176 mCallbackProxy.onProceededAfterSslError(sslError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001177 return;
1178 }
1179
1180 SslErrorHandler handler = new SslErrorHandler() {
Huahui Wuad053ce2010-12-08 15:24:55 -08001181 @Override
1182 public void proceed() {
Steve Blockbf52c0e2011-10-04 11:22:11 +01001183 SslCertLookupTable.getInstance().setIsAllowed(sslError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001184 nativeSslCertErrorProceed(handle);
1185 }
Huahui Wuad053ce2010-12-08 15:24:55 -08001186 @Override
1187 public void cancel() {
Steve Block200ff0a2011-09-27 12:51:18 +01001188 nativeSslCertErrorCancel(handle, certError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001189 }
1190 };
Steve Block200ff0a2011-09-27 12:51:18 +01001191 mCallbackProxy.onReceivedSslError(handler, sslError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001192 }
1193
1194 /**
Brian Carlstroma1477592011-02-11 13:39:56 -08001195 * Called by JNI when the native HTTPS stack gets a client
1196 * certificate request.
1197 *
1198 * We delegate the request to CallbackProxy, and route its response to
1199 * {@link #nativeSslClientCert(int, X509Certificate)}.
1200 */
Steve Block8eb83b92011-10-12 09:51:22 +01001201 private void requestClientCert(int handle, String hostAndPort) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001202 SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
Steve Block8eb83b92011-10-12 09:51:22 +01001203 if (table.IsAllowed(hostAndPort)) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001204 // previously allowed
1205 nativeSslClientCert(handle,
Steve Block8eb83b92011-10-12 09:51:22 +01001206 table.PrivateKey(hostAndPort),
1207 table.CertificateChain(hostAndPort));
1208 } else if (table.IsDenied(hostAndPort)) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001209 // previously denied
1210 nativeSslClientCert(handle, null, null);
1211 } else {
1212 // previously ignored or new
1213 mCallbackProxy.onReceivedClientCertRequest(
Steve Block8eb83b92011-10-12 09:51:22 +01001214 new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort);
Brian Carlstroma1477592011-02-11 13:39:56 -08001215 }
1216 }
1217
1218 /**
Iain Merrickd77f9b72010-10-18 18:22:25 +01001219 * Called by JNI when the native HTTP stack needs to download a file.
1220 *
1221 * We delegate the request to CallbackProxy, which owns the current app's
1222 * DownloadListener.
1223 */
1224 private void downloadStart(String url, String userAgent,
1225 String contentDisposition, String mimeType, long contentLength) {
Kristian Monsen9d005a12010-11-18 18:24:38 +00001226 // This will only work if the url ends with the filename
1227 if (mimeType.isEmpty()) {
1228 try {
1229 String extension = url.substring(url.lastIndexOf('.') + 1);
1230 mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension);
1231 // MimeUtils might return null, not sure if downloadmanager is happy with that
1232 if (mimeType == null)
1233 mimeType = "";
1234 } catch(IndexOutOfBoundsException exception) {
1235 // mimeType string end with a '.', not much to do
1236 }
1237 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001238 mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
1239 mimeType, url, contentDisposition);
Huahui Wuc7939b12011-01-26 22:11:02 -08001240
1241 if (CertTool.getCertType(mimeType) != null) {
1242 mKeyStoreHandler = new KeyStoreHandler(mimeType);
1243 } else {
1244 mCallbackProxy.onDownloadStart(url, userAgent,
Iain Merrickd77f9b72010-10-18 18:22:25 +01001245 contentDisposition, mimeType, contentLength);
Huahui Wuc7939b12011-01-26 22:11:02 -08001246 }
1247 }
1248
1249 /**
1250 * Called by JNI for Chrome HTTP stack when the Java side needs to access the data.
1251 */
1252 private void didReceiveData(byte data[], int size) {
1253 if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
1254 }
1255
1256 private void didFinishLoading() {
1257 if (mKeyStoreHandler != null) {
1258 mKeyStoreHandler.installCert(mContext);
1259 mKeyStoreHandler = null;
1260 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001261 }
1262
Iain Merrickb890e752011-01-20 15:33:41 +00001263 /**
Steve Blockfe33a752011-10-04 19:09:13 +01001264 * Called by JNI when we recieve a certificate for the page's main resource.
1265 * Used by the Chromium HTTP stack only.
Iain Merrickb890e752011-01-20 15:33:41 +00001266 */
Huahui Wuc56c3e22011-02-10 14:40:50 -08001267 private void setCertificate(byte cert_der[]) {
1268 try {
1269 X509Certificate cert = new X509CertImpl(cert_der);
1270 mCallbackProxy.onReceivedCertificate(new SslCertificate(cert));
1271 } catch (IOException e) {
1272 // Can't get the certificate, not much to do.
1273 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
1274 return;
1275 }
Iain Merrickb890e752011-01-20 15:33:41 +00001276 }
1277
Narayan Kamath9497c5f2011-02-22 12:05:34 +00001278 /*package*/ SearchBox getSearchBox() {
1279 return mSearchBox;
1280 }
1281
Patrick Scott85a50ff2011-01-25 14:42:12 -05001282 /**
1283 * Called by JNI when processing the X-Auto-Login header.
1284 */
1285 private void autoLogin(String realm, String account, String args) {
1286 mCallbackProxy.onReceivedLoginRequest(realm, account, args);
1287 }
1288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 //==========================================================================
1290 // native functions
1291 //==========================================================================
1292
1293 /**
1294 * Create a new native frame for a given WebView
1295 * @param w A WebView that the frame draws into.
1296 * @param am AssetManager to use to get assets.
1297 * @param list The native side will add and remove items from this list as
1298 * the native list changes.
1299 */
1300 private native void nativeCreateFrame(WebViewCore w, AssetManager am,
1301 WebBackForwardList list);
1302
1303 /**
1304 * Destroy the native frame.
1305 */
1306 public native void nativeDestroyFrame();
1307
1308 private native void nativeCallPolicyFunction(int policyFunction,
1309 int decision);
1310
1311 /**
1312 * Reload the current main frame.
1313 */
1314 public native void reload(boolean allowStale);
1315
1316 /**
1317 * Go back or forward the number of steps given.
1318 * @param steps A negative or positive number indicating the direction
1319 * and number of steps to move.
1320 */
1321 private native void nativeGoBackOrForward(int steps);
1322
1323 /**
1324 * stringByEvaluatingJavaScriptFromString will execute the
1325 * JS passed in in the context of this browser frame.
1326 * @param script A javascript string to execute
1327 *
1328 * @return string result of execution or null
1329 */
1330 public native String stringByEvaluatingJavaScriptFromString(String script);
1331
1332 /**
1333 * Add a javascript interface to the main frame.
1334 */
1335 private native void nativeAddJavascriptInterface(int nativeFramePointer,
1336 Object obj, String interfaceName);
1337
1338 /**
1339 * Enable or disable the native cache.
1340 */
1341 /* FIXME: The native cache is always on for now until we have a better
1342 * solution for our 2 caches. */
1343 private native void setCacheDisabled(boolean disabled);
1344
1345 public native boolean cacheDisabled();
1346
1347 public native void clearCache();
1348
1349 /**
1350 * Returns false if the url is bad.
1351 */
Grace Klobad0d9bc22010-01-26 18:08:28 -08001352 private native void nativeLoadUrl(String url, Map<String, String> headers);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353
Grace Kloba57534302009-05-22 18:55:02 -07001354 private native void nativePostUrl(String url, byte[] postData);
1355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 private native void nativeLoadData(String baseUrl, String data,
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001357 String mimeType, String encoding, String historyUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358
1359 /**
1360 * Stop loading the current page.
1361 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001362 public void stopLoading() {
1363 if (mIsMainFrame) {
1364 resetLoadingStates();
1365 }
1366 nativeStopLoading();
1367 }
1368
1369 private native void nativeStopLoading();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370
1371 /**
1372 * Return true if the document has images.
1373 */
1374 public native boolean documentHasImages();
1375
1376 /**
1377 * @return TRUE if there is a password field in the current frame
1378 */
1379 private native boolean hasPasswordField();
1380
1381 /**
1382 * Get username and password in the current frame. If found, String[0] is
1383 * username and String[1] is password. Otherwise return NULL.
1384 * @return String[]
1385 */
1386 private native String[] getUsernamePassword();
1387
1388 /**
1389 * Set username and password to the proper fields in the current frame
1390 * @param username
1391 * @param password
1392 */
1393 private native void setUsernamePassword(String username, String password);
1394
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -07001395 private native String nativeSaveWebArchive(String basename, boolean autoname);
1396
Patrick Scottf06364b2009-12-02 08:57:09 -05001397 private native void nativeOrientationChanged(int orientation);
Iain Merrick10229b22010-08-31 11:57:15 +01001398
1399 private native void nativeAuthenticationProceed(int handle, String username, String password);
1400 private native void nativeAuthenticationCancel(int handle);
Huahui Wuad053ce2010-12-08 15:24:55 -08001401
1402 private native void nativeSslCertErrorProceed(int handle);
Steve Block200ff0a2011-09-27 12:51:18 +01001403 private native void nativeSslCertErrorCancel(int handle, int certError);
Brian Carlstroma1477592011-02-11 13:39:56 -08001404
1405 native void nativeSslClientCert(int handle,
1406 byte[] pkcs8EncodedPrivateKey,
1407 byte[][] asn1DerEncodedCertificateChain);
George Mounte263bc12011-10-31 13:49:03 -07001408
1409 /**
1410 * Returns true when the contents of the frame is an RTL or vertical-rl
1411 * page. This is used for determining whether a frame should be initially
1412 * scrolled right-most as opposed to left-most.
1413 * @return true when the frame should be initially scrolled right-most
1414 * based on the text direction and writing mode.
1415 */
1416 /* package */ boolean getShouldStartScrolledRight() {
1417 return nativeGetShouldStartScrolledRight(mNativeFrame);
1418 }
1419
1420 private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421}