blob: c7a7374375a17f2ae34f449ae5f0901a395020ba [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;
Patrick Scott11f57382010-03-25 11:18:59 -040038import android.view.ViewRoot;
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;
Huahui Wuad053ce2010-12-08 15:24:55 -080047import java.security.cert.X509Certificate;
Patrick Scott11f57382010-03-25 11:18:59 -040048import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.HashMap;
Andrei Popescu4950b2b2009-09-03 13:56:07 +010050import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import java.util.Iterator;
52
Huahui Wuad053ce2010-12-08 15:24:55 -080053import org.apache.harmony.security.provider.cert.X509CertImpl;
54
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055class BrowserFrame extends Handler {
56
57 private static final String LOGTAG = "webkit";
58
59 /**
60 * Cap the number of LoadListeners that will be instantiated, so
61 * we don't blow the GREF count. Attempting to queue more than
62 * this many requests will prompt an error() callback on the
63 * request's LoadListener
64 */
65 private final static int MAX_OUTSTANDING_REQUESTS = 300;
66
67 private final CallbackProxy mCallbackProxy;
68 private final WebSettings mSettings;
69 private final Context mContext;
70 private final WebViewDatabase mDatabase;
71 private final WebViewCore mWebViewCore;
72 /* package */ boolean mLoadInitFromJava;
73 private int mLoadType;
74 private boolean mFirstLayoutDone = true;
75 private boolean mCommitted = true;
Leon Clarkec0b778e2010-03-15 15:27:02 +000076 // Flag for blocking messages. This is used during destroy() so
77 // that if the UI thread posts any messages after the message
78 // queue has been cleared,they are ignored.
79 private boolean mBlockMessages = false;
Shimeng (Simon) Wang2edfcd52010-10-11 13:39:17 -070080 private int mOrientation = -1;
Kristian Monsenedb20ac2010-06-16 16:00:37 +010081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 // Is this frame the main frame?
83 private boolean mIsMainFrame;
84
85 // Attached Javascript interfaces
Andrei Popescu4950b2b2009-09-03 13:56:07 +010086 private Map<String, Object> mJSInterfaceMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
Huahui Wuc7939b12011-01-26 22:11:02 -080088 // Key store handler when Chromium HTTP stack is used.
89 private KeyStoreHandler mKeyStoreHandler = null;
90
Narayan Kamath9497c5f2011-02-22 12:05:34 +000091 // Implementation of the searchbox API.
92 private final SearchBoxImpl mSearchBox;
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 // message ids
95 // a message posted when a frame loading is completed
96 static final int FRAME_COMPLETED = 1001;
Patrick Scottf06364b2009-12-02 08:57:09 -050097 // orientation change message
98 static final int ORIENTATION_CHANGED = 1002;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 // a message posted when the user decides the policy
100 static final int POLICY_FUNCTION = 1003;
101
102 // Note: need to keep these in sync with FrameLoaderTypes.h in native
103 static final int FRAME_LOADTYPE_STANDARD = 0;
104 static final int FRAME_LOADTYPE_BACK = 1;
105 static final int FRAME_LOADTYPE_FORWARD = 2;
106 static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3;
107 static final int FRAME_LOADTYPE_RELOAD = 4;
108 static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5;
109 static final int FRAME_LOADTYPE_SAME = 6;
110 static final int FRAME_LOADTYPE_REDIRECT = 7;
111 static final int FRAME_LOADTYPE_REPLACE = 8;
112
113 // A progress threshold to switch from history Picture to live Picture
114 private static final int TRANSITION_SWITCH_THRESHOLD = 75;
115
116 // This is a field accessed by native code as well as package classes.
117 /*package*/ int mNativeFrame;
118
119 // Static instance of a JWebCoreJavaBridge to handle timer and cookie
120 // requests from WebCore.
121 static JWebCoreJavaBridge sJavaBridge;
122
Patrick Scott11f57382010-03-25 11:18:59 -0400123 private static class ConfigCallback implements ComponentCallbacks {
124 private final ArrayList<WeakReference<Handler>> mHandlers =
125 new ArrayList<WeakReference<Handler>>();
126 private final WindowManager mWindowManager;
127
128 ConfigCallback(WindowManager wm) {
129 mWindowManager = wm;
130 }
131
132 public synchronized void addHandler(Handler h) {
133 // No need to ever remove a Handler. If the BrowserFrame is
134 // destroyed, it will be collected and the WeakReference set to
135 // null. If it happens to still be around during a configuration
136 // change, the message will be ignored.
137 mHandlers.add(new WeakReference<Handler>(h));
138 }
139
140 public void onConfigurationChanged(Configuration newConfig) {
141 if (mHandlers.size() == 0) {
142 return;
143 }
144 int orientation =
145 mWindowManager.getDefaultDisplay().getOrientation();
146 switch (orientation) {
147 case Surface.ROTATION_90:
148 orientation = 90;
149 break;
150 case Surface.ROTATION_180:
151 orientation = 180;
152 break;
153 case Surface.ROTATION_270:
154 orientation = -90;
155 break;
156 case Surface.ROTATION_0:
157 orientation = 0;
158 break;
159 default:
160 break;
161 }
162 synchronized (this) {
163 // Create a list of handlers to remove. Go ahead and make it
164 // the same size to avoid resizing.
165 ArrayList<WeakReference> handlersToRemove =
166 new ArrayList<WeakReference>(mHandlers.size());
167 for (WeakReference<Handler> wh : mHandlers) {
168 Handler h = wh.get();
169 if (h != null) {
170 h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED,
171 orientation, 0));
172 } else {
173 handlersToRemove.add(wh);
174 }
175 }
176 // Now remove all the null references.
177 for (WeakReference weak : handlersToRemove) {
178 mHandlers.remove(weak);
179 }
180 }
181 }
182
183 public void onLowMemory() {}
184 }
185 static ConfigCallback sConfigCallback;
186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 /**
188 * Create a new BrowserFrame to be used in an application.
189 * @param context An application context to use when retrieving assets.
190 * @param w A WebViewCore used as the view for this frame.
191 * @param proxy A CallbackProxy for posting messages to the UI thread and
192 * querying a client for information.
193 * @param settings A WebSettings object that holds all settings.
194 * XXX: Called by WebCore thread.
195 */
196 public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100197 WebSettings settings, Map<String, Object> javascriptInterfaces) {
Romain Guy01d0fbf2009-12-01 14:52:19 -0800198
199 Context appContext = context.getApplicationContext();
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // Create a global JWebCoreJavaBridge to handle timers and
202 // cookies in the WebCore thread.
203 if (sJavaBridge == null) {
Grace Kloba9b95ab12010-04-01 16:07:30 -0700204 sJavaBridge = new JWebCoreJavaBridge();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 // set WebCore native cache size
Grace Kloba11843132009-10-08 20:48:09 -0700206 ActivityManager am = (ActivityManager) context
207 .getSystemService(Context.ACTIVITY_SERVICE);
208 if (am.getMemoryClass() > 16) {
209 sJavaBridge.setCacheSize(8 * 1024 * 1024);
210 } else {
211 sJavaBridge.setCacheSize(4 * 1024 * 1024);
212 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 // initialize CacheManager
Romain Guy01d0fbf2009-12-01 14:52:19 -0800214 CacheManager.init(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 // create CookieSyncManager with current Context
Romain Guy01d0fbf2009-12-01 14:52:19 -0800216 CookieSyncManager.createInstance(appContext);
Grace Kloba658ab7d2009-05-14 14:45:26 -0700217 // create PluginManager with current Context
Romain Guy01d0fbf2009-12-01 14:52:19 -0800218 PluginManager.getInstance(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 }
Patrick Scott11f57382010-03-25 11:18:59 -0400220
221 if (sConfigCallback == null) {
222 sConfigCallback = new ConfigCallback(
Patrick Scott624051a2011-01-05 13:36:00 -0500223 (WindowManager) appContext.getSystemService(
Patrick Scott11f57382010-03-25 11:18:59 -0400224 Context.WINDOW_SERVICE));
225 ViewRoot.addConfigCallback(sConfigCallback);
226 }
227 sConfigCallback.addHandler(this);
228
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100229 mJSInterfaceMap = javascriptInterfaces;
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000230 if (mJSInterfaceMap == null) {
231 mJSInterfaceMap = new HashMap<String, Object>();
232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233
234 mSettings = settings;
235 mContext = context;
236 mCallbackProxy = proxy;
Romain Guy01d0fbf2009-12-01 14:52:19 -0800237 mDatabase = WebViewDatabase.getInstance(appContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 mWebViewCore = w;
239
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000240 mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
241 mJSInterfaceMap.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
242
Grace Kloba83031582009-09-02 16:21:42 -0700243 AssetManager am = context.getAssets();
244 nativeCreateFrame(w, am, proxy.getBackForwardList());
245
Derek Sollenberger2e5c1502009-06-03 10:44:42 -0400246 if (DebugFlags.BROWSER_FRAME) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
248 }
249 }
250
251 /**
252 * Load a url from the network or the filesystem into the main frame.
Grace Klobad0d9bc22010-01-26 18:08:28 -0800253 * Following the same behaviour as Safari, javascript: URLs are not passed
254 * to the main frame, instead they are evaluated immediately.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 * @param url The url to load.
Grace Klobad0d9bc22010-01-26 18:08:28 -0800256 * @param extraHeaders The extra headers sent with this url. This should not
257 * include the common headers like "user-agent". If it does, it
258 * will be replaced by the intrinsic value of the WebView.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 */
Grace Klobad0d9bc22010-01-26 18:08:28 -0800260 public void loadUrl(String url, Map<String, String> extraHeaders) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 mLoadInitFromJava = true;
262 if (URLUtil.isJavaScriptUrl(url)) {
263 // strip off the scheme and evaluate the string
264 stringByEvaluatingJavaScriptFromString(
265 url.substring("javascript:".length()));
266 } else {
Grace Klobad0d9bc22010-01-26 18:08:28 -0800267 nativeLoadUrl(url, extraHeaders);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 }
269 mLoadInitFromJava = false;
270 }
271
272 /**
Grace Kloba57534302009-05-22 18:55:02 -0700273 * Load a url with "POST" method from the network into the main frame.
274 * @param url The url to load.
275 * @param data The data for POST request.
276 */
277 public void postUrl(String url, byte[] data) {
278 mLoadInitFromJava = true;
279 nativePostUrl(url, data);
280 mLoadInitFromJava = false;
281 }
282
283 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 * Load the content as if it was loaded by the provided base URL. The
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400285 * historyUrl is used as the history entry for the load data.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 *
287 * @param baseUrl Base URL used to resolve relative paths in the content
288 * @param data Content to render in the browser
289 * @param mimeType Mimetype of the data being passed in
290 * @param encoding Character set encoding of the provided data.
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400291 * @param historyUrl URL to use as the history entry.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 */
293 public void loadData(String baseUrl, String data, String mimeType,
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400294 String encoding, String historyUrl) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 mLoadInitFromJava = true;
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400296 if (historyUrl == null || historyUrl.length() == 0) {
297 historyUrl = "about:blank";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299 if (data == null) {
300 data = "";
301 }
302
303 // Setup defaults for missing values. These defaults where taken from
304 // WebKit's WebFrame.mm
305 if (baseUrl == null || baseUrl.length() == 0) {
306 baseUrl = "about:blank";
307 }
308 if (mimeType == null || mimeType.length() == 0) {
309 mimeType = "text/html";
310 }
Leon Scroggins1bb1a912010-03-23 15:39:46 -0400311 nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 mLoadInitFromJava = false;
313 }
314
315 /**
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -0700316 * Saves the contents of the frame as a web archive.
317 *
318 * @param basename The filename where the archive should be placed.
319 * @param autoname If false, takes filename to be a file. If true, filename
320 * is assumed to be a directory in which a filename will be
321 * chosen according to the url of the current page.
322 */
323 /* package */ String saveWebArchive(String basename, boolean autoname) {
324 return nativeSaveWebArchive(basename, autoname);
325 }
326
327 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 * Go back or forward the number of steps given.
329 * @param steps A negative or positive number indicating the direction
330 * and number of steps to move.
331 */
332 public void goBackOrForward(int steps) {
333 mLoadInitFromJava = true;
334 nativeGoBackOrForward(steps);
335 mLoadInitFromJava = false;
336 }
337
338 /**
339 * native callback
340 * Report an error to an activity.
341 * @param errorCode The HTTP error code.
Iain Merrick83d4a232010-11-12 14:11:16 +0000342 * @param description Optional human-readable description. If no description
343 * is given, we'll use a standard localized error message.
344 * @param failingUrl The URL that was being loaded when the error occurred.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 * TODO: Report all errors including resource errors but include some kind
346 * of domain identifier. Change errorCode to an enum for a cleaner
347 * interface.
348 */
Iain Merrick83d4a232010-11-12 14:11:16 +0000349 private void reportError(int errorCode, String description, String failingUrl) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 // As this is called for the main resource and loading will be stopped
351 // after, reset the state variables.
The Android Open Source Project4df24232009-03-05 14:34:35 -0800352 resetLoadingStates();
Iain Merrick83d4a232010-11-12 14:11:16 +0000353 if (description == null || description.isEmpty()) {
354 description = ErrorStrings.getString(errorCode, mContext);
355 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800356 mCallbackProxy.onReceivedError(errorCode, description, failingUrl);
357 }
358
359 private void resetLoadingStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 mCommitted = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 mFirstLayoutDone = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363
364 /* package */boolean committed() {
365 return mCommitted;
366 }
367
368 /* package */boolean firstLayoutDone() {
369 return mFirstLayoutDone;
370 }
371
372 /* package */int loadType() {
373 return mLoadType;
374 }
375
376 /* package */void didFirstLayout() {
377 if (!mFirstLayoutDone) {
378 mFirstLayoutDone = true;
379 // ensure {@link WebViewCore#webkitDraw} is called as we were
380 // blocking the update in {@link #loadStarted}
381 mWebViewCore.contentDraw();
382 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 }
384
385 /**
386 * native callback
387 * Indicates the beginning of a new load.
388 * This method will be called once for the main frame.
389 */
390 private void loadStarted(String url, Bitmap favicon, int loadType,
391 boolean isMainFrame) {
392 mIsMainFrame = isMainFrame;
393
394 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
395 mLoadType = loadType;
396
397 if (isMainFrame) {
398 // Call onPageStarted for main frames.
399 mCallbackProxy.onPageStarted(url, favicon);
400 // as didFirstLayout() is only called for the main frame, reset
401 // mFirstLayoutDone only for the main frames
402 mFirstLayoutDone = false;
403 mCommitted = false;
404 // remove pending draw to block update until mFirstLayoutDone is
405 // set to true in didFirstLayout()
406 mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW);
407 }
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500408 }
409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500411 @SuppressWarnings("unused")
412 private void saveFormData(HashMap<String, String> data) {
413 if (mSettings.getSaveFormData()) {
414 final WebHistoryItem h = mCallbackProxy.getBackForwardList()
415 .getCurrentItem();
416 if (h != null) {
417 String currentUrl = h.getUrl();
418 if (currentUrl != null) {
419 mDatabase.setFormData(currentUrl, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 }
421 }
422 }
423 }
424
Leon Scroggins2f7fa832011-02-03 16:32:11 -0500425 @SuppressWarnings("unused")
426 private boolean shouldSaveFormData() {
427 if (mSettings.getSaveFormData()) {
428 final WebHistoryItem h = mCallbackProxy.getBackForwardList()
429 .getCurrentItem();
430 return h != null && h.getUrl() != null;
431 }
432 return false;
433 }
434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 /**
436 * native callback
437 * Indicates the WebKit has committed to the new load
438 */
439 private void transitionToCommitted(int loadType, boolean isMainFrame) {
440 // loadType is not used yet
441 if (isMainFrame) {
442 mCommitted = true;
Grace Kloba9a67c822009-12-20 11:33:58 -0800443 mWebViewCore.getWebView().mViewManager.postResetStateAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 }
445 }
446
447 /**
448 * native callback
449 * <p>
450 * Indicates the end of a new load.
451 * This method will be called once for the main frame.
452 */
453 private void loadFinished(String url, int loadType, boolean isMainFrame) {
454 // mIsMainFrame and isMainFrame are better be equal!!!
455
456 if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) {
457 if (isMainFrame) {
The Android Open Source Project4df24232009-03-05 14:34:35 -0800458 resetLoadingStates();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 mCallbackProxy.switchOutDrawHistory();
460 mCallbackProxy.onPageFinished(url);
461 }
462 }
463 }
464
465 /**
466 * We have received an SSL certificate for the main top-level page.
467 *
468 * !!!Called from the network thread!!!
469 */
470 void certificate(SslCertificate certificate) {
471 if (mIsMainFrame) {
472 // we want to make this call even if the certificate is null
473 // (ie, the site is not secure)
474 mCallbackProxy.onReceivedCertificate(certificate);
475 }
476 }
477
478 /**
479 * Destroy all native components of the BrowserFrame.
480 */
481 public void destroy() {
482 nativeDestroyFrame();
Leon Clarkec0b778e2010-03-15 15:27:02 +0000483 mBlockMessages = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 removeCallbacksAndMessages(null);
485 }
486
487 /**
488 * Handle messages posted to us.
489 * @param msg The message to handle.
490 */
491 @Override
492 public void handleMessage(Message msg) {
Leon Clarkec0b778e2010-03-15 15:27:02 +0000493 if (mBlockMessages) {
494 return;
495 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 switch (msg.what) {
497 case FRAME_COMPLETED: {
498 if (mSettings.getSavePassword() && hasPasswordField()) {
Patrick Scottaf31c3a2009-08-24 13:46:09 -0400499 WebHistoryItem item = mCallbackProxy.getBackForwardList()
500 .getCurrentItem();
501 if (item != null) {
502 WebAddress uri = new WebAddress(item.getUrl());
Bjorn Bringerteb8be972010-10-12 16:24:55 +0100503 String schemePlusHost = uri.getScheme() + uri.getHost();
Patrick Scottaf31c3a2009-08-24 13:46:09 -0400504 String[] up =
505 mDatabase.getUsernamePassword(schemePlusHost);
506 if (up != null && up[0] != null) {
507 setUsernamePassword(up[0], up[1]);
508 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 }
510 }
Steve Block808751f2011-01-04 14:26:27 +0000511 if (!JniUtil.useChromiumHttpStack()) {
512 WebViewWorker.getHandler().sendEmptyMessage(
513 WebViewWorker.MSG_TRIM_CACHE);
514 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 break;
516 }
517
518 case POLICY_FUNCTION: {
519 nativeCallPolicyFunction(msg.arg1, msg.arg2);
520 break;
521 }
522
Patrick Scottf06364b2009-12-02 08:57:09 -0500523 case ORIENTATION_CHANGED: {
Shimeng (Simon) Wang5f7c5a22010-10-11 13:12:48 -0700524 if (mOrientation != msg.arg1) {
525 mOrientation = msg.arg1;
526 nativeOrientationChanged(msg.arg1);
527 }
Patrick Scottf06364b2009-12-02 08:57:09 -0500528 break;
529 }
530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 default:
532 break;
533 }
534 }
535
536 /**
537 * Punch-through for WebCore to set the document
538 * title. Inform the Activity of the new title.
539 * @param title The new title of the document.
540 */
541 private void setTitle(String title) {
542 // FIXME: The activity must call getTitle (a native method) to get the
543 // title. We should try and cache the title if we can also keep it in
544 // sync with the document.
545 mCallbackProxy.onReceivedTitle(title);
546 }
547
548 /**
549 * Retrieves the render tree of this frame and puts it as the object for
550 * the message and sends the message.
551 * @param callback the message to use to send the render tree
552 */
553 public void externalRepresentation(Message callback) {
554 callback.obj = externalRepresentation();;
555 callback.sendToTarget();
556 }
557
558 /**
559 * Return the render tree as a string
560 */
561 private native String externalRepresentation();
562
563 /**
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700564 * Retrieves the visual text of the frames, puts it as the object for
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 * the message and sends the message.
566 * @param callback the message to use to send the visual text
567 */
568 public void documentAsText(Message callback) {
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700569 StringBuilder text = new StringBuilder();
570 if (callback.arg1 != 0) {
571 // Dump top frame as text.
572 text.append(documentAsText());
573 }
574 if (callback.arg2 != 0) {
575 // Dump child frames as text.
576 text.append(childFramesAsText());
577 }
578 callback.obj = text.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 callback.sendToTarget();
580 }
581
582 /**
583 * Return the text drawn on the screen as a string
584 */
585 private native String documentAsText();
586
Shimeng (Simon) Wangad456862010-06-16 17:32:24 -0700587 /**
588 * Return the text drawn on the child frames as a string
589 */
590 private native String childFramesAsText();
591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 /*
593 * This method is called by WebCore to inform the frame that
594 * the Javascript window object has been cleared.
595 * We should re-attach any attached js interfaces.
596 */
597 private void windowObjectCleared(int nativeFramePointer) {
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000598 Iterator<String> iter = mJSInterfaceMap.keySet().iterator();
599 while (iter.hasNext()) {
600 String interfaceName = iter.next();
601 Object object = mJSInterfaceMap.get(interfaceName);
602 if (object != null) {
603 nativeAddJavascriptInterface(nativeFramePointer,
604 mJSInterfaceMap.get(interfaceName), interfaceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 }
606 }
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000607
608 stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
610
611 /**
612 * This method is called by WebCore to check whether application
613 * wants to hijack url loading
614 */
615 public boolean handleUrl(String url) {
616 if (mLoadInitFromJava == true) {
617 return false;
618 }
619 if (mCallbackProxy.shouldOverrideUrlLoading(url)) {
620 // if the url is hijacked, reset the state of the BrowserFrame
621 didFirstLayout();
622 return true;
623 } else {
624 return false;
625 }
626 }
627
628 public void addJavascriptInterface(Object obj, String interfaceName) {
Steve Block544295e2010-12-02 18:40:06 +0000629 assert obj != null;
Steve Block689a3422010-12-07 18:18:26 +0000630 removeJavascriptInterface(interfaceName);
Narayan Kamath9497c5f2011-02-22 12:05:34 +0000631
Steve Block689a3422010-12-07 18:18:26 +0000632 mJSInterfaceMap.put(interfaceName, obj);
633 }
634
635 public void removeJavascriptInterface(String interfaceName) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 if (mJSInterfaceMap.containsKey(interfaceName)) {
637 mJSInterfaceMap.remove(interfaceName);
638 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 }
640
641 /**
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400642 * Called by JNI. Given a URI, find the associated file and return its size
643 * @param uri A String representing the URI of the desired file.
644 * @return int The size of the given file.
645 */
646 private int getFileSize(String uri) {
647 int size = 0;
Cary Clarkac96fa52010-03-22 10:56:37 -0400648 try {
649 InputStream stream = mContext.getContentResolver()
650 .openInputStream(Uri.parse(uri));
651 size = stream.available();
652 stream.close();
653 } catch (Exception e) {}
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400654 return size;
655 }
656
657 /**
658 * Called by JNI. Given a URI, a buffer, and an offset into the buffer,
659 * copy the resource into buffer.
660 * @param uri A String representing the URI of the desired file.
661 * @param buffer The byte array to copy the data into.
662 * @param offset The offet into buffer to place the data.
Romain Guy01d0fbf2009-12-01 14:52:19 -0800663 * @param expectedSize The size that the buffer has allocated for this file.
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400664 * @return int The size of the given file, or zero if it fails.
665 */
Leon Scrogginsd5ba82a2009-10-13 14:23:56 -0400666 private int getFile(String uri, byte[] buffer, int offset,
667 int expectedSize) {
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400668 int size = 0;
669 try {
670 InputStream stream = mContext.getContentResolver()
671 .openInputStream(Uri.parse(uri));
672 size = stream.available();
Leon Scrogginsd5ba82a2009-10-13 14:23:56 -0400673 if (size <= expectedSize && buffer != null
674 && buffer.length - offset >= size) {
Leon Scroggins70ca3c22009-10-02 15:58:55 -0400675 stream.read(buffer, offset, size);
676 } else {
677 size = 0;
678 }
679 stream.close();
680 } catch (java.io.FileNotFoundException e) {
681 Log.e(LOGTAG, "FileNotFoundException:" + e);
682 size = 0;
683 } catch (java.io.IOException e2) {
684 Log.e(LOGTAG, "IOException: " + e2);
685 size = 0;
686 }
687 return size;
688 }
689
690 /**
Kristian Monsendba35c02010-08-19 20:22:53 +0100691 * Get the InputStream for an Android resource
692 * There are three different kinds of android resources:
693 * - file:///android_res
694 * - file:///android_asset
695 * - content://
696 * @param url The url to load.
697 * @return An InputStream to the android resource
698 */
Kristian Monsenbadf9452010-11-01 15:49:45 +0000699 private InputStream inputStreamForAndroidResource(String url) {
700 // This list needs to be kept in sync with the list in
701 // external/webkit/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
702 final String ANDROID_ASSET = "file:///android_asset/";
703 final String ANDROID_RESOURCE = "file:///android_res/";
704 final String ANDROID_CONTENT = "content:";
Kristian Monsendba35c02010-08-19 20:22:53 +0100705
Kristian Monsenbadf9452010-11-01 15:49:45 +0000706 // file:///android_res
707 if (url.startsWith(ANDROID_RESOURCE)) {
708 url = url.replaceFirst(ANDROID_RESOURCE, "");
Kristian Monsendba35c02010-08-19 20:22:53 +0100709 if (url == null || url.length() == 0) {
710 Log.e(LOGTAG, "url has length 0 " + url);
711 return null;
712 }
713 int slash = url.indexOf('/');
714 int dot = url.indexOf('.', slash);
715 if (slash == -1 || dot == -1) {
716 Log.e(LOGTAG, "Incorrect res path: " + url);
717 return null;
718 }
719 String subClassName = url.substring(0, slash);
720 String fieldName = url.substring(slash + 1, dot);
721 String errorMsg = null;
722 try {
723 final Class<?> d = mContext.getApplicationContext()
724 .getClassLoader().loadClass(
725 mContext.getPackageName() + ".R$"
726 + subClassName);
727 final java.lang.reflect.Field field = d.getField(fieldName);
728 final int id = field.getInt(null);
729 TypedValue value = new TypedValue();
730 mContext.getResources().getValue(id, value, true);
731 if (value.type == TypedValue.TYPE_STRING) {
732 return mContext.getAssets().openNonAsset(
733 value.assetCookie, value.string.toString(),
734 AssetManager.ACCESS_STREAMING);
735 } else {
736 // Old stack only supports TYPE_STRING for res files
737 Log.e(LOGTAG, "not of type string: " + url);
738 return null;
739 }
740 } catch (Exception e) {
741 Log.e(LOGTAG, "Exception: " + url);
742 return null;
743 }
744
Kristian Monsenbadf9452010-11-01 15:49:45 +0000745 // file:///android_asset
746 } else if (url.startsWith(ANDROID_ASSET)) {
747 url = url.replaceFirst(ANDROID_ASSET, "");
Kristian Monsendba35c02010-08-19 20:22:53 +0100748 try {
749 AssetManager assets = mContext.getAssets();
750 return assets.open(url, AssetManager.ACCESS_STREAMING);
751 } catch (IOException e) {
752 return null;
753 }
Kristian Monsenbadf9452010-11-01 15:49:45 +0000754
755 // content://
Patrick Scottd1737ed2011-01-05 11:36:48 -0500756 } else if (mSettings.getAllowContentAccess() &&
757 url.startsWith(ANDROID_CONTENT)) {
Kristian Monsendba35c02010-08-19 20:22:53 +0100758 try {
Iain Merrick2bcd2922010-10-27 12:33:09 +0100759 // Strip off mimetype, for compatibility with ContentLoader.java
760 // If we don't do this, we can fail to load Gmail attachments,
761 // because the URL being loaded doesn't exactly match the URL we
762 // have permission to read.
763 int mimeIndex = url.lastIndexOf('?');
764 if (mimeIndex != -1) {
765 url = url.substring(0, mimeIndex);
766 }
Kristian Monsendba35c02010-08-19 20:22:53 +0100767 Uri uri = Uri.parse(url);
768 return mContext.getContentResolver().openInputStream(uri);
769 } catch (Exception e) {
770 Log.e(LOGTAG, "Exception: " + url);
771 return null;
772 }
773 } else {
774 return null;
775 }
776 }
777
778 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 * Start loading a resource.
780 * @param loaderHandle The native ResourceLoader that is the target of the
781 * data.
782 * @param url The url to load.
783 * @param method The http method.
784 * @param headers The http headers.
785 * @param postData If the method is "POST" postData is sent as the request
786 * body. Is null when empty.
Brian Carlstromdba8cb72010-03-18 16:56:41 -0700787 * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0.
788 * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode
789 * @param mainResource True if the this resource is the main request, not a supporting resource
790 * @param userGesture
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 * @param synchronous True if the load is synchronous.
792 * @return A newly created LoadListener object.
793 */
794 private LoadListener startLoadingResource(int loaderHandle,
795 String url,
796 String method,
797 HashMap headers,
798 byte[] postData,
Grace Kloba8c92c392009-11-08 19:01:55 -0800799 long postDataIdentifier,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 int cacheMode,
Patrick Scott7c24ed62009-11-30 13:34:40 -0500801 boolean mainResource,
802 boolean userGesture,
Steve Blockea08c512010-03-24 16:24:25 +0000803 boolean synchronous,
804 String username,
805 String password) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 PerfChecker checker = new PerfChecker();
807
808 if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) {
809 cacheMode = mSettings.getCacheMode();
810 }
811
812 if (method.equals("POST")) {
813 // Don't use the cache on POSTs when issuing a normal POST
814 // request.
815 if (cacheMode == WebSettings.LOAD_NORMAL) {
816 cacheMode = WebSettings.LOAD_NO_CACHE;
817 }
Iain Merrick9eb2a892010-11-18 15:32:31 +0000818 String[] ret = getUsernamePassword();
819 if (ret != null) {
820 String domUsername = ret[0];
821 String domPassword = ret[1];
822 maybeSavePassword(postData, domUsername, domPassword);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 }
824 }
825
826 // is this resource the main-frame top-level page?
827 boolean isMainFramePage = mIsMainFrame;
828
Derek Sollenberger2e5c1502009-06-03 10:44:42 -0400829 if (DebugFlags.BROWSER_FRAME) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
Patrick Scottfe4fec72009-07-14 15:54:30 -0400831 + method + ", postData=" + postData + ", isMainFramePage="
Patrick Scott7c24ed62009-11-30 13:34:40 -0500832 + isMainFramePage + ", mainResource=" + mainResource
833 + ", userGesture=" + userGesture);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
835
836 // Create a LoadListener
Grace Kloba8c92c392009-11-08 19:01:55 -0800837 LoadListener loadListener = LoadListener.getLoadListener(mContext,
838 this, url, loaderHandle, synchronous, isMainFramePage,
Steve Blockea08c512010-03-24 16:24:25 +0000839 mainResource, userGesture, postDataIdentifier, username, password);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) {
Grace Klobafadbbd22009-07-17 20:39:25 -0700842 // send an error message, so that loadListener can be deleted
843 // after this is returned. This is important as LoadListener's
844 // nativeError will remove the request from its DocLoader's request
845 // list. But the set up is not done until this method is returned.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 loadListener.error(
847 android.net.http.EventHandler.ERROR, mContext.getString(
848 com.android.internal.R.string.httpErrorTooManyRequests));
Grace Klobafadbbd22009-07-17 20:39:25 -0700849 return loadListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 }
851
Patrick Scottc12544a2010-11-11 13:16:44 -0500852 // Note that we are intentionally skipping
853 // inputStreamForAndroidResource. This is so that FrameLoader will use
854 // the various StreamLoader classes to handle assets.
855 FrameLoader loader = new FrameLoader(loadListener, mSettings, method,
856 mCallbackProxy.shouldInterceptRequest(url));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 loader.setHeaders(headers);
858 loader.setPostData(postData);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800859 // Set the load mode to the mode used for the current page.
860 // If WebKit wants validation, go to network directly.
861 loader.setCacheMode(headers.containsKey("If-Modified-Since")
862 || headers.containsKey("If-None-Match") ?
863 WebSettings.LOAD_NO_CACHE : cacheMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 // Set referrer to current URL?
865 if (!loader.executeLoad()) {
866 checker.responseAlert("startLoadingResource fail");
867 }
868 checker.responseAlert("startLoadingResource succeed");
869
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 return !synchronous ? loadListener : null;
871 }
872
Iain Merrick9eb2a892010-11-18 15:32:31 +0000873 /**
874 * If this looks like a POST request (form submission) containing a username
875 * and password, give the user the option of saving them. Will either do
876 * nothing, or block until the UI interaction is complete.
877 *
878 * Called by startLoadingResource when using the Apache HTTP stack.
879 * Called directly by WebKit when using the Chrome HTTP stack.
880 *
881 * @param postData The data about to be sent as the body of a POST request.
882 * @param username The username entered by the user (sniffed from the DOM).
883 * @param password The password entered by the user (sniffed from the DOM).
884 */
885 private void maybeSavePassword(
886 byte[] postData, String username, String password) {
887 if (postData == null
888 || username == null || username.isEmpty()
889 || password == null || password.isEmpty()) {
890 return; // No password to save.
891 }
892
893 if (!mSettings.getSavePassword()) {
894 return; // User doesn't want to save passwords.
895 }
896
897 try {
898 if (DebugFlags.BROWSER_FRAME) {
899 Assert.assertNotNull(mCallbackProxy.getBackForwardList()
900 .getCurrentItem());
901 }
902 WebAddress uri = new WebAddress(mCallbackProxy
903 .getBackForwardList().getCurrentItem().getUrl());
904 String schemePlusHost = uri.getScheme() + uri.getHost();
905 // Check to see if the username & password appear in
906 // the post data (there could be another form on the
907 // page and that was posted instead.
908 String postString = new String(postData);
909 if (postString.contains(URLEncoder.encode(username)) &&
910 postString.contains(URLEncoder.encode(password))) {
911 String[] saved = mDatabase.getUsernamePassword(
912 schemePlusHost);
913 if (saved != null) {
914 // null username implies that user has chosen not to
915 // save password
916 if (saved[0] != null) {
917 // non-null username implies that user has
918 // chosen to save password, so update the
919 // recorded password
920 mDatabase.setUsernamePassword(
921 schemePlusHost, username, password);
922 }
923 } else {
924 // CallbackProxy will handle creating the resume
925 // message
926 mCallbackProxy.onSavePassword(schemePlusHost, username,
927 password, null);
928 }
929 }
930 } catch (ParseException ex) {
931 // if it is bad uri, don't save its password
932 }
933 }
934
Patrick Scottc12544a2010-11-11 13:16:44 -0500935 // Called by jni from the chrome network stack.
936 private WebResourceResponse shouldInterceptRequest(String url) {
937 InputStream androidResource = inputStreamForAndroidResource(url);
938 if (androidResource != null) {
939 return new WebResourceResponse(null, null, androidResource);
940 }
John Reckd6662b52011-02-16 14:21:21 -0800941 WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url);
942 if (response == null && "browser:incognito".equals(url)) {
943 try {
944 Resources res = mContext.getResources();
945 InputStream ins = res.openRawResource(
946 com.android.internal.R.raw.incognito_mode_start_page);
947 response = new WebResourceResponse("text/html", "utf8", ins);
948 } catch (NotFoundException ex) {
949 // This shouldn't happen, but try and gracefully handle it jic
950 Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex);
951 }
952 }
953 return response;
Patrick Scottc12544a2010-11-11 13:16:44 -0500954 }
955
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 /**
957 * Set the progress for the browser activity. Called by native code.
958 * Uses a delay so it does not happen too often.
959 * @param newProgress An int between zero and one hundred representing
960 * the current progress percentage of loading the page.
961 */
962 private void setProgress(int newProgress) {
963 mCallbackProxy.onProgressChanged(newProgress);
964 if (newProgress == 100) {
965 sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100);
966 }
967 // FIXME: Need to figure out a better way to switch out of the history
968 // drawing mode. Maybe we can somehow compare the history picture with
969 // the current picture, and switch when it contains more content.
970 if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) {
971 mCallbackProxy.switchOutDrawHistory();
972 }
973 }
974
975 /**
976 * Send the icon to the activity for display.
977 * @param icon A Bitmap representing a page's favicon.
978 */
979 private void didReceiveIcon(Bitmap icon) {
980 mCallbackProxy.onReceivedIcon(icon);
981 }
982
Patrick Scott2ba12622009-08-04 13:20:05 -0400983 // Called by JNI when an apple-touch-icon attribute was found.
Patrick Scottd58ccff2009-09-18 16:29:00 -0400984 private void didReceiveTouchIconUrl(String url, boolean precomposed) {
985 mCallbackProxy.onReceivedTouchIconUrl(url, precomposed);
Patrick Scott2ba12622009-08-04 13:20:05 -0400986 }
987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 /**
989 * Request a new window from the client.
990 * @return The BrowserFrame object stored in the new WebView.
991 */
992 private BrowserFrame createWindow(boolean dialog, boolean userGesture) {
Patrick Scott97147282010-06-16 12:27:06 -0400993 return mCallbackProxy.createWindow(dialog, userGesture);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
995
996 /**
997 * Try to focus this WebView.
998 */
999 private void requestFocus() {
1000 mCallbackProxy.onRequestFocus();
1001 }
1002
1003 /**
1004 * Close this frame and window.
1005 */
1006 private void closeWindow(WebViewCore w) {
1007 mCallbackProxy.onCloseWindow(w.getWebView());
1008 }
1009
1010 // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore
1011 static final int POLICY_USE = 0;
1012 static final int POLICY_IGNORE = 2;
1013
1014 private void decidePolicyForFormResubmission(int policyFunction) {
1015 Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction,
1016 POLICY_IGNORE);
1017 Message resend = obtainMessage(POLICY_FUNCTION, policyFunction,
1018 POLICY_USE);
1019 mCallbackProxy.onFormResubmission(dontResend, resend);
1020 }
1021
1022 /**
1023 * Tell the activity to update its global history.
1024 */
1025 private void updateVisitedHistory(String url, boolean isReload) {
1026 mCallbackProxy.doUpdateVisitedHistory(url, isReload);
1027 }
1028
1029 /**
1030 * Get the CallbackProxy for sending messages to the UI thread.
1031 */
1032 /* package */ CallbackProxy getCallbackProxy() {
1033 return mCallbackProxy;
1034 }
1035
1036 /**
1037 * Returns the User Agent used by this frame
1038 */
1039 String getUserAgentString() {
1040 return mSettings.getUserAgentString();
1041 }
1042
Leon Scrogginsaacced62009-12-11 15:01:22 -05001043 // These ids need to be in sync with enum rawResId in PlatformBridge.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 private static final int NODOMAIN = 1;
1045 private static final int LOADERROR = 2;
Grace Kloba83031582009-09-02 16:21:42 -07001046 private static final int DRAWABLEDIR = 3;
Leon Scrogginsaacced62009-12-11 15:01:22 -05001047 private static final int FILE_UPLOAD_LABEL = 4;
1048 private static final int RESET_LABEL = 5;
1049 private static final int SUBMIT_LABEL = 6;
Ben Murdochfa296a42010-05-18 16:16:40 +01001050 private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051
1052 String getRawResFilename(int id) {
1053 int resid;
1054 switch (id) {
1055 case NODOMAIN:
1056 resid = com.android.internal.R.raw.nodomain;
1057 break;
1058
1059 case LOADERROR:
1060 resid = com.android.internal.R.raw.loaderror;
1061 break;
1062
Grace Kloba83031582009-09-02 16:21:42 -07001063 case DRAWABLEDIR:
1064 // use one known resource to find the drawable directory
1065 resid = com.android.internal.R.drawable.btn_check_off;
1066 break;
1067
Leon Scrogginsaacced62009-12-11 15:01:22 -05001068 case FILE_UPLOAD_LABEL:
1069 return mContext.getResources().getString(
1070 com.android.internal.R.string.upload_file);
1071
1072 case RESET_LABEL:
1073 return mContext.getResources().getString(
1074 com.android.internal.R.string.reset);
1075
1076 case SUBMIT_LABEL:
1077 return mContext.getResources().getString(
1078 com.android.internal.R.string.submit);
1079
Ben Murdochfa296a42010-05-18 16:16:40 +01001080 case FILE_UPLOAD_NO_FILE_CHOSEN:
1081 return mContext.getResources().getString(
1082 com.android.internal.R.string.no_file_chosen);
1083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 default:
1085 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
Cary Clark686cf752009-08-11 16:08:52 -04001086 return "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 }
1088 TypedValue value = new TypedValue();
1089 mContext.getResources().getValue(resid, value, true);
Grace Kloba83031582009-09-02 16:21:42 -07001090 if (id == DRAWABLEDIR) {
1091 String path = value.string.toString();
1092 int index = path.lastIndexOf('/');
1093 if (index < 0) {
1094 Log.e(LOGTAG, "Can't find drawable directory.");
1095 return "";
1096 }
1097 return path.substring(0, index + 1);
1098 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 return value.string.toString();
1100 }
1101
Grace Kloba408cf852009-09-20 16:34:44 -07001102 private float density() {
1103 return mContext.getResources().getDisplayMetrics().density;
1104 }
1105
Iain Merrick10229b22010-08-31 11:57:15 +01001106 /**
1107 * Called by JNI when the native HTTP stack gets an authentication request.
1108 *
1109 * We delegate the request to CallbackProxy, and route its response to
1110 * {@link #nativeAuthenticationProceed(int, String, String)} or
1111 * {@link #nativeAuthenticationCancel(int)}.
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001112 *
1113 * We don't care what thread the callback is invoked on. All threading is
1114 * handled on the C++ side, because the WebKit thread may be blocked on a
1115 * synchronous call and unable to pump our MessageQueue.
Iain Merrick10229b22010-08-31 11:57:15 +01001116 */
1117 private void didReceiveAuthenticationChallenge(
1118 final int handle, String host, String realm, final boolean useCachedCredentials) {
1119
1120 HttpAuthHandler handler = new HttpAuthHandler() {
1121
Iain Merrick10229b22010-08-31 11:57:15 +01001122 @Override
1123 public boolean useHttpAuthUsernamePassword() {
1124 return useCachedCredentials;
1125 }
1126
1127 @Override
1128 public void proceed(String username, String password) {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001129 nativeAuthenticationProceed(handle, username, password);
Iain Merrick10229b22010-08-31 11:57:15 +01001130 }
1131
1132 @Override
1133 public void cancel() {
Iain Merrick4f0e56b2010-09-23 16:22:47 +01001134 nativeAuthenticationCancel(handle);
Iain Merrick10229b22010-08-31 11:57:15 +01001135 }
1136 };
1137 mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
1138 }
1139
Iain Merrickd77f9b72010-10-18 18:22:25 +01001140 /**
Huahui Wuc56c3e22011-02-10 14:40:50 -08001141 * Called by JNI when the native HTTP(S) stack gets an invalid cert chain.
Huahui Wuad053ce2010-12-08 15:24:55 -08001142 *
1143 * We delegate the request to CallbackProxy, and route its response to
1144 * {@link #nativeSslCertErrorProceed(int)} or
1145 * {@link #nativeSslCertErrorCancel(int, int)}.
1146 */
1147 private void reportSslCertError(final int handle, final int cert_error, byte cert_der[]) {
1148 final SslError ssl_error;
1149 try {
1150 X509Certificate cert = new X509CertImpl(cert_der);
1151 ssl_error = new SslError(cert_error, cert);
1152 } catch (IOException e) {
Huahui Wuc56c3e22011-02-10 14:40:50 -08001153 // Can't get the certificate, not much to do.
1154 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
Huahui Wuad053ce2010-12-08 15:24:55 -08001155 nativeSslCertErrorCancel(handle, cert_error);
1156 return;
1157 }
1158
1159 SslErrorHandler handler = new SslErrorHandler() {
1160
1161 @Override
1162 public void proceed() {
1163 SslCertLookupTable.getInstance().Allow(ssl_error);
1164 nativeSslCertErrorProceed(handle);
1165 }
1166
1167 @Override
1168 public void cancel() {
1169 SslCertLookupTable.getInstance().Deny(ssl_error);
1170 nativeSslCertErrorCancel(handle, cert_error);
1171 }
1172 };
1173
1174 if (SslCertLookupTable.getInstance().IsAllowed(ssl_error)) {
1175 nativeSslCertErrorProceed(handle);
1176 } else {
1177 mCallbackProxy.onReceivedSslError(handler, ssl_error);
1178 }
1179 }
1180
1181 /**
Iain Merrickd77f9b72010-10-18 18:22:25 +01001182 * Called by JNI when the native HTTP stack needs to download a file.
1183 *
1184 * We delegate the request to CallbackProxy, which owns the current app's
1185 * DownloadListener.
1186 */
1187 private void downloadStart(String url, String userAgent,
1188 String contentDisposition, String mimeType, long contentLength) {
Kristian Monsen9d005a12010-11-18 18:24:38 +00001189 // This will only work if the url ends with the filename
1190 if (mimeType.isEmpty()) {
1191 try {
1192 String extension = url.substring(url.lastIndexOf('.') + 1);
1193 mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension);
1194 // MimeUtils might return null, not sure if downloadmanager is happy with that
1195 if (mimeType == null)
1196 mimeType = "";
1197 } catch(IndexOutOfBoundsException exception) {
1198 // mimeType string end with a '.', not much to do
1199 }
1200 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001201 mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
1202 mimeType, url, contentDisposition);
Huahui Wuc7939b12011-01-26 22:11:02 -08001203
1204 if (CertTool.getCertType(mimeType) != null) {
1205 mKeyStoreHandler = new KeyStoreHandler(mimeType);
1206 } else {
1207 mCallbackProxy.onDownloadStart(url, userAgent,
Iain Merrickd77f9b72010-10-18 18:22:25 +01001208 contentDisposition, mimeType, contentLength);
Huahui Wuc7939b12011-01-26 22:11:02 -08001209 }
1210 }
1211
1212 /**
1213 * Called by JNI for Chrome HTTP stack when the Java side needs to access the data.
1214 */
1215 private void didReceiveData(byte data[], int size) {
1216 if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
1217 }
1218
1219 private void didFinishLoading() {
1220 if (mKeyStoreHandler != null) {
1221 mKeyStoreHandler.installCert(mContext);
1222 mKeyStoreHandler = null;
1223 }
Iain Merrickd77f9b72010-10-18 18:22:25 +01001224 }
1225
Iain Merrickb890e752011-01-20 15:33:41 +00001226 /**
1227 * Called by JNI when we load a page over SSL.
1228 */
Huahui Wuc56c3e22011-02-10 14:40:50 -08001229 private void setCertificate(byte cert_der[]) {
1230 try {
1231 X509Certificate cert = new X509CertImpl(cert_der);
1232 mCallbackProxy.onReceivedCertificate(new SslCertificate(cert));
1233 } catch (IOException e) {
1234 // Can't get the certificate, not much to do.
1235 Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
1236 return;
1237 }
Iain Merrickb890e752011-01-20 15:33:41 +00001238 }
1239
Narayan Kamath9497c5f2011-02-22 12:05:34 +00001240 /*package*/ SearchBox getSearchBox() {
1241 return mSearchBox;
1242 }
1243
Patrick Scott85a50ff2011-01-25 14:42:12 -05001244 /**
1245 * Called by JNI when processing the X-Auto-Login header.
1246 */
1247 private void autoLogin(String realm, String account, String args) {
1248 mCallbackProxy.onReceivedLoginRequest(realm, account, args);
1249 }
1250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 //==========================================================================
1252 // native functions
1253 //==========================================================================
1254
1255 /**
1256 * Create a new native frame for a given WebView
1257 * @param w A WebView that the frame draws into.
1258 * @param am AssetManager to use to get assets.
1259 * @param list The native side will add and remove items from this list as
1260 * the native list changes.
1261 */
1262 private native void nativeCreateFrame(WebViewCore w, AssetManager am,
1263 WebBackForwardList list);
1264
1265 /**
1266 * Destroy the native frame.
1267 */
1268 public native void nativeDestroyFrame();
1269
1270 private native void nativeCallPolicyFunction(int policyFunction,
1271 int decision);
1272
1273 /**
1274 * Reload the current main frame.
1275 */
1276 public native void reload(boolean allowStale);
1277
1278 /**
1279 * Go back or forward the number of steps given.
1280 * @param steps A negative or positive number indicating the direction
1281 * and number of steps to move.
1282 */
1283 private native void nativeGoBackOrForward(int steps);
1284
1285 /**
1286 * stringByEvaluatingJavaScriptFromString will execute the
1287 * JS passed in in the context of this browser frame.
1288 * @param script A javascript string to execute
1289 *
1290 * @return string result of execution or null
1291 */
1292 public native String stringByEvaluatingJavaScriptFromString(String script);
1293
1294 /**
1295 * Add a javascript interface to the main frame.
1296 */
1297 private native void nativeAddJavascriptInterface(int nativeFramePointer,
1298 Object obj, String interfaceName);
1299
1300 /**
1301 * Enable or disable the native cache.
1302 */
1303 /* FIXME: The native cache is always on for now until we have a better
1304 * solution for our 2 caches. */
1305 private native void setCacheDisabled(boolean disabled);
1306
1307 public native boolean cacheDisabled();
1308
1309 public native void clearCache();
1310
1311 /**
1312 * Returns false if the url is bad.
1313 */
Grace Klobad0d9bc22010-01-26 18:08:28 -08001314 private native void nativeLoadUrl(String url, Map<String, String> headers);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315
Grace Kloba57534302009-05-22 18:55:02 -07001316 private native void nativePostUrl(String url, byte[] postData);
1317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 private native void nativeLoadData(String baseUrl, String data,
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001319 String mimeType, String encoding, String historyUrl);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320
1321 /**
1322 * Stop loading the current page.
1323 */
The Android Open Source Project4df24232009-03-05 14:34:35 -08001324 public void stopLoading() {
1325 if (mIsMainFrame) {
1326 resetLoadingStates();
1327 }
1328 nativeStopLoading();
1329 }
1330
1331 private native void nativeStopLoading();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332
1333 /**
1334 * Return true if the document has images.
1335 */
1336 public native boolean documentHasImages();
1337
1338 /**
1339 * @return TRUE if there is a password field in the current frame
1340 */
1341 private native boolean hasPasswordField();
1342
1343 /**
1344 * Get username and password in the current frame. If found, String[0] is
1345 * username and String[1] is password. Otherwise return NULL.
1346 * @return String[]
1347 */
1348 private native String[] getUsernamePassword();
1349
1350 /**
1351 * Set username and password to the proper fields in the current frame
1352 * @param username
1353 * @param password
1354 */
1355 private native void setUsernamePassword(String username, String password);
1356
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -07001357 private native String nativeSaveWebArchive(String basename, boolean autoname);
1358
Patrick Scottf06364b2009-12-02 08:57:09 -05001359 private native void nativeOrientationChanged(int orientation);
Iain Merrick10229b22010-08-31 11:57:15 +01001360
1361 private native void nativeAuthenticationProceed(int handle, String username, String password);
1362 private native void nativeAuthenticationCancel(int handle);
Huahui Wuad053ce2010-12-08 15:24:55 -08001363
1364 private native void nativeSslCertErrorProceed(int handle);
1365 private native void nativeSslCertErrorCancel(int handle, int cert_error);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366}