blob: 6fddb1ab90b7a436e534e0d4c221d792f68acbd0 [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);
John Reck430b1b92012-01-04 09:49:17 -0800873 loader.executeLoad();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 // Set referrer to current URL?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 return !synchronous ? loadListener : null;
876 }
877
Iain Merrick9eb2a892010-11-18 15:32:31 +0000878 /**
879 * If this looks like a POST request (form submission) containing a username
880 * and password, give the user the option of saving them. Will either do
881 * nothing, or block until the UI interaction is complete.
882 *
883 * Called by startLoadingResource when using the Apache HTTP stack.
884 * Called directly by WebKit when using the Chrome HTTP stack.
885 *
886 * @param postData The data about to be sent as the body of a POST request.
887 * @param username The username entered by the user (sniffed from the DOM).
888 * @param password The password entered by the user (sniffed from the DOM).
889 */
890 private void maybeSavePassword(
891 byte[] postData, String username, String password) {
892 if (postData == null
893 || username == null || username.isEmpty()
894 || password == null || password.isEmpty()) {
895 return; // No password to save.
896 }
897
898 if (!mSettings.getSavePassword()) {
899 return; // User doesn't want to save passwords.
900 }
901
902 try {
903 if (DebugFlags.BROWSER_FRAME) {
904 Assert.assertNotNull(mCallbackProxy.getBackForwardList()
905 .getCurrentItem());
906 }
907 WebAddress uri = new WebAddress(mCallbackProxy
908 .getBackForwardList().getCurrentItem().getUrl());
909 String schemePlusHost = uri.getScheme() + uri.getHost();
910 // Check to see if the username & password appear in
911 // the post data (there could be another form on the
912 // page and that was posted instead.
913 String postString = new String(postData);
914 if (postString.contains(URLEncoder.encode(username)) &&
915 postString.contains(URLEncoder.encode(password))) {
916 String[] saved = mDatabase.getUsernamePassword(
917 schemePlusHost);
918 if (saved != null) {
919 // null username implies that user has chosen not to
920 // save password
921 if (saved[0] != null) {
922 // non-null username implies that user has
923 // chosen to save password, so update the
924 // recorded password
925 mDatabase.setUsernamePassword(
926 schemePlusHost, username, password);
927 }
928 } else {
929 // CallbackProxy will handle creating the resume
930 // message
931 mCallbackProxy.onSavePassword(schemePlusHost, username,
932 password, null);
933 }
934 }
935 } catch (ParseException ex) {
936 // if it is bad uri, don't save its password
937 }
938 }
939
Patrick Scottc12544a2010-11-11 13:16:44 -0500940 // Called by jni from the chrome network stack.
941 private WebResourceResponse shouldInterceptRequest(String url) {
942 InputStream androidResource = inputStreamForAndroidResource(url);
943 if (androidResource != null) {
944 return new WebResourceResponse(null, null, androidResource);
945 }
Ben Murdoch2c18ede2011-12-16 18:08:30 +0000946
947 // Note that we check this after looking for an android_asset or
948 // android_res URL, as we allow those even if file access is disabled.
949 if (!mSettings.getAllowFileAccess() && url.startsWith("file://")) {
950 return new WebResourceResponse(null, null, null);
951 }
952
John Reckd6662b52011-02-16 14:21:21 -0800953 WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url);
954 if (response == null && "browser:incognito".equals(url)) {
955 try {
956 Resources res = mContext.getResources();
957 InputStream ins = res.openRawResource(
958 com.android.internal.R.raw.incognito_mode_start_page);
959 response = new WebResourceResponse("text/html", "utf8", ins);
960 } catch (NotFoundException ex) {
961 // This shouldn't happen, but try and gracefully handle it jic
962 Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex);
963 }
964 }
965 return response;
Patrick Scottc12544a2010-11-11 13:16:44 -0500966 }
967
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 /**
969 * Set the progress for the browser activity. Called by native code.
970 * Uses a delay so it does not happen too often.
971 * @param newProgress An int between zero and one hundred representing
972 * the current progress percentage of loading the page.
973 */
974 private void setProgress(int newProgress) {
975 mCallbackProxy.onProgressChanged(newProgress);
976 if (newProgress == 100) {
977 sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100);
978 }
979 // FIXME: Need to figure out a better way to switch out of the history
980 // drawing mode. Maybe we can somehow compare the history picture with
981 // the current picture, and switch when it contains more content.
982 if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) {
983 mCallbackProxy.switchOutDrawHistory();
984 }
985 }
986
987 /**
988 * Send the icon to the activity for display.
989 * @param icon A Bitmap representing a page's favicon.
990 */
991 private void didReceiveIcon(Bitmap icon) {
992 mCallbackProxy.onReceivedIcon(icon);
993 }
994
Patrick Scott2ba12622009-08-04 13:20:05 -0400995 // Called by JNI when an apple-touch-icon attribute was found.
Patrick Scottd58ccff2009-09-18 16:29:00 -0400996 private void didReceiveTouchIconUrl(String url, boolean precomposed) {
997 mCallbackProxy.onReceivedTouchIconUrl(url, precomposed);
Patrick Scott2ba12622009-08-04 13:20:05 -0400998 }
999
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 /**
1001 * Request a new window from the client.
1002 * @return The BrowserFrame object stored in the new WebView.
1003 */
1004 private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
Patrick Scott97147282010-06-16 12:27:06 -04001005 return mCallbackProxy.createWindow(dialog, userGesture);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 }
1007
1008 /**
1009 * Try to focus this WebView.
1010 */
1011 private void requestFocus() {
1012 mCallbackProxy.onRequestFocus();
1013 }
1014
1015 /**
1016 * Close this frame and window.
1017 */
1018 private void closeWindow(WebViewCore w) {
1019 mCallbackProxy.onCloseWindow(w.getWebView());
1020 }
1021
1022 // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore
1023 static final int POLICY_USE = 0;
1024 static final int POLICY_IGNORE = 2;
1025
1026 private void decidePolicyForFormResubmission(int policyFunction) {
1027 Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction,
1028 POLICY_IGNORE);
1029 Message resend = obtainMessage(POLICY_FUNCTION, policyFunction,
1030 POLICY_USE);
1031 mCallbackProxy.onFormResubmission(dontResend, resend);
1032 }
1033
1034 /**
1035 * Tell the activity to update its global history.
1036 */
1037 private void updateVisitedHistory(String url, boolean isReload) {
1038 mCallbackProxy.doUpdateVisitedHistory(url, isReload);
1039 }
1040
1041 /**
1042 * Get the CallbackProxy for sending messages to the UI thread.
1043 */
1044 /* package */ CallbackProxy getCallbackProxy() {
1045 return mCallbackProxy;
1046 }
1047
1048 /**
1049 * Returns the User Agent used by this frame
1050 */
1051 String getUserAgentString() {
1052 return mSettings.getUserAgentString();
1053 }
1054
Leon Scrogginsaacced62009-12-11 15:01:22 -05001055 // These ids need to be in sync with enum rawResId in PlatformBridge.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 private static final int NODOMAIN = 1;
1057 private static final int LOADERROR = 2;
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001058 /* package */ static final int DRAWABLEDIR = 3;
Leon Scrogginsaacced62009-12-11 15:01:22 -05001059 private static final int FILE_UPLOAD_LABEL = 4;
1060 private static final int RESET_LABEL = 5;
1061 private static final int SUBMIT_LABEL = 6;
Ben Murdochfa296a42010-05-18 16:16:40 +01001062 private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001064 private String getRawResFilename(int id) {
1065 return getRawResFilename(id, mContext);
1066 }
1067 /* package */ static String getRawResFilename(int id, Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 int resid;
1069 switch (id) {
1070 case NODOMAIN:
1071 resid = com.android.internal.R.raw.nodomain;
1072 break;
1073
1074 case LOADERROR:
1075 resid = com.android.internal.R.raw.loaderror;
1076 break;
1077
Grace Kloba83031582009-09-02 16:21:42 -07001078 case DRAWABLEDIR:
1079 // use one known resource to find the drawable directory
1080 resid = com.android.internal.R.drawable.btn_check_off;
1081 break;
1082
Leon Scrogginsaacced62009-12-11 15:01:22 -05001083 case FILE_UPLOAD_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.upload_file);
1086
1087 case RESET_LABEL:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001088 return context.getResources().getString(
Leon Scrogginsaacced62009-12-11 15:01:22 -05001089 com.android.internal.R.string.reset);
1090
1091 case SUBMIT_LABEL:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001092 return context.getResources().getString(
Leon Scrogginsaacced62009-12-11 15:01:22 -05001093 com.android.internal.R.string.submit);
1094
Ben Murdochfa296a42010-05-18 16:16:40 +01001095 case FILE_UPLOAD_NO_FILE_CHOSEN:
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001096 return context.getResources().getString(
Ben Murdochfa296a42010-05-18 16:16:40 +01001097 com.android.internal.R.string.no_file_chosen);
1098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 default:
1100 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
Cary Clark686cf752009-08-11 16:08:52 -04001101 return "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 }
1103 TypedValue value = new TypedValue();
Leon Scrogginscdaff15bd2011-03-04 12:30:03 -05001104 context.getResources().getValue(resid, value, true);
Grace Kloba83031582009-09-02 16:21:42 -07001105 if (id == DRAWABLEDIR) {
1106 String path = value.string.toString();
1107 int index = path.lastIndexOf('/');
1108 if (index < 0) {
1109 Log.e(LOGTAG, "Can't find drawable directory.");
1110 return "";
1111 }
1112 return path.substring(0, index + 1);
1113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 return value.string.toString();
1115 }
1116
Grace Kloba408cf852009-09-20 16:34:44 -07001117 private float density() {
1118 return mContext.getResources().getDisplayMetrics().density;
1119 }
1120
Iain Merrick10229b22010-08-31 11:57:15 +01001121 /**
1122 * Called by JNI when the native HTTP stack gets an authentication request.
1123 *
1124 * We delegate the request to CallbackProxy, and route its response to
1125 * {@link #nativeAuthenticationProceed(int, String, String)} or
1126 * {@link #nativeAuthenticationCancel(int)}.
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001127 *
1128 * We don't care what thread the callback is invoked on. All threading is
1129 * handled on the C++ side, because the WebKit thread may be blocked on a
1130 * synchronous call and unable to pump our MessageQueue.
Iain Merrick10229b22010-08-31 11:57:15 +01001131 */
1132 private void didReceiveAuthenticationChallenge(
Ben Murdocha7f0ed42011-08-05 15:46:52 +01001133 final int handle, String host, String realm, final boolean useCachedCredentials,
1134 final boolean suppressDialog) {
Iain Merrick10229b22010-08-31 11:57:15 +01001135
1136 HttpAuthHandler handler = new HttpAuthHandler() {
1137
Iain Merrick10229b22010-08-31 11:57:15 +01001138 @Override
1139 public boolean useHttpAuthUsernamePassword() {
1140 return useCachedCredentials;
1141 }
1142
1143 @Override
1144 public void proceed(String username, String password) {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001145 nativeAuthenticationProceed(handle, username, password);
Iain Merrick10229b22010-08-31 11:57:15 +01001146 }
1147
1148 @Override
1149 public void cancel() {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001150 nativeAuthenticationCancel(handle);
Iain Merrick10229b22010-08-31 11:57:15 +01001151 }
Ben Murdocha7f0ed42011-08-05 15:46:52 +01001152
1153 @Override
1154 public boolean suppressDialog() {
1155 return suppressDialog;
1156 }
Iain Merrick10229b22010-08-31 11:57:15 +01001157 };
1158 mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
1159 }
1160
Iain Merrickd77f9b72010-10-18 18:22:25 +01001161 /**
Steve Block200ff0a2011-09-27 12:51:18 +01001162 * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain.
Huahui Wuad053ce2010-12-08 15:24:55 -08001163 *
1164 * We delegate the request to CallbackProxy, and route its response to
1165 * {@link #nativeSslCertErrorProceed(int)} or
1166 * {@link #nativeSslCertErrorCancel(int, int)}.
1167 */
Steve Block200ff0a2011-09-27 12:51:18 +01001168 private void reportSslCertError(final int handle, final int certError, byte certDER[],
1169 String url) {
1170 final SslError sslError;
Huahui Wuad053ce2010-12-08 15:24:55 -08001171 try {
Steve Block200ff0a2011-09-27 12:51:18 +01001172 X509Certificate cert = new X509CertImpl(certDER);
Kristian Monsen0d6c6a72011-07-13 20:24:39 +01001173 SslCertificate sslCert = new SslCertificate(cert);
Steve Blockfa03f9a2011-10-05 12:45:37 +01001174 sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url);
Huahui Wuad053ce2010-12-08 15:24:55 -08001175 } catch (IOException e) {
Huahui Wuc56c3e22011-02-10 14:40:50 -08001176 // Can't get the certificate, not much to do.
1177 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
Steve Block200ff0a2011-09-27 12:51:18 +01001178 nativeSslCertErrorCancel(handle, certError);
1179 return;
1180 }
1181
1182 if (SslCertLookupTable.getInstance().isAllowed(sslError)) {
1183 nativeSslCertErrorProceed(handle);
Steve Blockfe33a752011-10-04 19:09:13 +01001184 mCallbackProxy.onProceededAfterSslError(sslError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001185 return;
1186 }
1187
1188 SslErrorHandler handler = new SslErrorHandler() {
Huahui Wuad053ce2010-12-08 15:24:55 -08001189 @Override
1190 public void proceed() {
Steve Blockbf52c0e2011-10-04 11:22:11 +01001191 SslCertLookupTable.getInstance().setIsAllowed(sslError);
Selim Gurun38f36102011-12-07 18:39:24 -08001192 post(new Runnable() {
1193 public void run() {
1194 nativeSslCertErrorProceed(handle);
1195 }
1196 });
Huahui Wuad053ce2010-12-08 15:24:55 -08001197 }
Huahui Wuad053ce2010-12-08 15:24:55 -08001198 @Override
1199 public void cancel() {
Selim Gurun38f36102011-12-07 18:39:24 -08001200 post(new Runnable() {
1201 public void run() {
1202 nativeSslCertErrorCancel(handle, certError);
1203 }
1204 });
Huahui Wuad053ce2010-12-08 15:24:55 -08001205 }
1206 };
Steve Block200ff0a2011-09-27 12:51:18 +01001207 mCallbackProxy.onReceivedSslError(handler, sslError);
Huahui Wuad053ce2010-12-08 15:24:55 -08001208 }
1209
1210 /**
Brian Carlstroma1477592011-02-11 13:39:56 -08001211 * Called by JNI when the native HTTPS stack gets a client
1212 * certificate request.
1213 *
1214 * We delegate the request to CallbackProxy, and route its response to
1215 * {@link #nativeSslClientCert(int, X509Certificate)}.
1216 */
Steve Block8eb83b92011-10-12 09:51:22 +01001217 private void requestClientCert(int handle, String hostAndPort) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001218 SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
Steve Block8eb83b92011-10-12 09:51:22 +01001219 if (table.IsAllowed(hostAndPort)) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001220 // previously allowed
1221 nativeSslClientCert(handle,
Steve Block8eb83b92011-10-12 09:51:22 +01001222 table.PrivateKey(hostAndPort),
1223 table.CertificateChain(hostAndPort));
1224 } else if (table.IsDenied(hostAndPort)) {
Brian Carlstroma1477592011-02-11 13:39:56 -08001225 // previously denied
1226 nativeSslClientCert(handle, null, null);
1227 } else {
1228 // previously ignored or new
1229 mCallbackProxy.onReceivedClientCertRequest(
Steve Block8eb83b92011-10-12 09:51:22 +01001230 new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort);
Brian Carlstroma1477592011-02-11 13:39:56 -08001231 }
1232 }
1233
1234 /**
Iain Merrickd77f9b72010-10-18 18:22:25 +01001235 * Called by JNI when the native HTTP stack needs to download a file.
1236 *
1237 * We delegate the request to CallbackProxy, which owns the current app's
1238 * DownloadListener.
1239 */
1240 private void downloadStart(String url, String userAgent,
1241 String contentDisposition, String mimeType, long contentLength) {
Kristian Monsen9d005a12010-11-18 18:24:38 +00001242 // This will only work if the url ends with the filename
1243 if (mimeType.isEmpty()) {
1244 try {
1245 String extension = url.substring(url.lastIndexOf('.') + 1);
1246 mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension);
1247 // MimeUtils might return null, not sure if downloadmanager is happy with that
1248 if (mimeType == null)
1249 mimeType = "";
1250 } catch(IndexOutOfBoundsException exception) {
1251 // mimeType string end with a '.', not much to do
1252 }
1253 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001254 mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
1255 mimeType, url, contentDisposition);
Huahui Wuc7939b12011-01-26 22:11:02 -08001256
1257 if (CertTool.getCertType(mimeType) != null) {
1258 mKeyStoreHandler = new KeyStoreHandler(mimeType);
1259 } else {
1260 mCallbackProxy.onDownloadStart(url, userAgent,
Iain Merrickd77f9b72010-10-18 18:22:25 +01001261 contentDisposition, mimeType, contentLength);
Huahui Wuc7939b12011-01-26 22:11:02 -08001262 }
1263 }
1264
1265 /**
1266 * Called by JNI for Chrome HTTP stack when the Java side needs to access the data.
1267 */
1268 private void didReceiveData(byte data[], int size) {
1269 if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
1270 }
1271
1272 private void didFinishLoading() {
1273 if (mKeyStoreHandler != null) {
1274 mKeyStoreHandler.installCert(mContext);
1275 mKeyStoreHandler = null;
1276 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001277 }
1278
Iain Merrickb890e752011-01-20 15:33:41 +00001279 /**
Steve Blockfe33a752011-10-04 19:09:13 +01001280 * Called by JNI when we recieve a certificate for the page's main resource.
1281 * Used by the Chromium HTTP stack only.
Iain Merrickb890e752011-01-20 15:33:41 +00001282 */
Huahui Wuc56c3e22011-02-10 14:40:50 -08001283 private void setCertificate(byte cert_der[]) {
1284 try {
1285 X509Certificate cert = new X509CertImpl(cert_der);
1286 mCallbackProxy.onReceivedCertificate(new SslCertificate(cert));
1287 } catch (IOException e) {
1288 // Can't get the certificate, not much to do.
1289 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
1290 return;
1291 }
Iain Merrickb890e752011-01-20 15:33:41 +00001292 }
1293
Narayan Kamath9497c5f2011-02-22 12:05:34 +00001294 /*package*/ SearchBox getSearchBox() {
1295 return mSearchBox;
1296 }
1297
Patrick Scott85a50ff2011-01-25 14:42:12 -05001298 /**
1299 * Called by JNI when processing the X-Auto-Login header.
1300 */
1301 private void autoLogin(String realm, String account, String args) {
1302 mCallbackProxy.onReceivedLoginRequest(realm, account, args);
1303 }
1304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 //==========================================================================
1306 // native functions
1307 //==========================================================================
1308
1309 /**
1310 * Create a new native frame for a given WebView
1311 * @param w A WebView that the frame draws into.
1312 * @param am AssetManager to use to get assets.
1313 * @param list The native side will add and remove items from this list as
1314 * the native list changes.
1315 */
1316 private native void nativeCreateFrame(WebViewCore w, AssetManager am,
1317 WebBackForwardList list);
1318
1319 /**
1320 * Destroy the native frame.
1321 */
1322 public native void nativeDestroyFrame();
1323
1324 private native void nativeCallPolicyFunction(int policyFunction,
1325 int decision);
1326
1327 /**
1328 * Reload the current main frame.
1329 */
1330 public native void reload(boolean allowStale);
1331
1332 /**
1333 * Go back or forward the number of steps given.
1334 * @param steps A negative or positive number indicating the direction
1335 * and number of steps to move.
1336 */
1337 private native void nativeGoBackOrForward(int steps);
1338
1339 /**
1340 * stringByEvaluatingJavaScriptFromString will execute the
1341 * JS passed in in the context of this browser frame.
1342 * @param script A javascript string to execute
1343 *
1344 * @return string result of execution or null
1345 */
1346 public native String stringByEvaluatingJavaScriptFromString(String script);
1347
1348 /**
1349 * Add a javascript interface to the main frame.
1350 */
1351 private native void nativeAddJavascriptInterface(int nativeFramePointer,
1352 Object obj, String interfaceName);
1353
1354 /**
1355 * Enable or disable the native cache.
1356 */
1357 /* FIXME: The native cache is always on for now until we have a better
1358 * solution for our 2 caches. */
1359 private native void setCacheDisabled(boolean disabled);
1360
1361 public native boolean cacheDisabled();
1362
1363 public native void clearCache();
1364
1365 /**
1366 * Returns false if the url is bad.
1367 */
Grace Klobad0d9bc22010-01-26 18:08:28 -08001368 private native void nativeLoadUrl(String url, Map<String, String> headers);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369
Grace Kloba57534302009-05-22 18:55:02 -07001370 private native void nativePostUrl(String url, byte[] postData);
1371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 private native void nativeLoadData(String baseUrl, String data,
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001373 String mimeType, String encoding, String historyUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374
1375 /**
1376 * Stop loading the current page.
1377 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001378 public void stopLoading() {
1379 if (mIsMainFrame) {
1380 resetLoadingStates();
1381 }
1382 nativeStopLoading();
1383 }
1384
1385 private native void nativeStopLoading();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386
1387 /**
1388 * Return true if the document has images.
1389 */
1390 public native boolean documentHasImages();
1391
1392 /**
1393 * @return TRUE if there is a password field in the current frame
1394 */
1395 private native boolean hasPasswordField();
1396
1397 /**
1398 * Get username and password in the current frame. If found, String[0] is
1399 * username and String[1] is password. Otherwise return NULL.
1400 * @return String[]
1401 */
1402 private native String[] getUsernamePassword();
1403
1404 /**
1405 * Set username and password to the proper fields in the current frame
1406 * @param username
1407 * @param password
1408 */
1409 private native void setUsernamePassword(String username, String password);
1410
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -07001411 private native String nativeSaveWebArchive(String basename, boolean autoname);
1412
Patrick Scottf06364b2009-12-02 08:57:09 -05001413 private native void nativeOrientationChanged(int orientation);
Iain Merrick10229b22010-08-31 11:57:15 +01001414
1415 private native void nativeAuthenticationProceed(int handle, String username, String password);
1416 private native void nativeAuthenticationCancel(int handle);
Huahui Wuad053ce2010-12-08 15:24:55 -08001417
1418 private native void nativeSslCertErrorProceed(int handle);
Steve Block200ff0a2011-09-27 12:51:18 +01001419 private native void nativeSslCertErrorCancel(int handle, int certError);
Brian Carlstroma1477592011-02-11 13:39:56 -08001420
1421 native void nativeSslClientCert(int handle,
1422 byte[] pkcs8EncodedPrivateKey,
1423 byte[][] asn1DerEncodedCertificateChain);
George Mounte263bc12011-10-31 13:49:03 -07001424
1425 /**
1426 * Returns true when the contents of the frame is an RTL or vertical-rl
1427 * page. This is used for determining whether a frame should be initially
1428 * scrolled right-most as opposed to left-most.
1429 * @return true when the frame should be initially scrolled right-most
1430 * based on the text direction and writing mode.
1431 */
1432 /* package */ boolean getShouldStartScrolledRight() {
1433 return nativeGetShouldStartScrolledRight(mNativeFrame);
1434 }
1435
1436 private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437}