blob: 3e79edbcb9a5bea5249c3c675b3df8ffe79453fb [file] [log] [blame]
Grace Kloba22ac16e2009-10-07 18:00:23 -07001/*
2 * Copyright (C) 2009 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 com.android.browser;
18
Jeff Hamilton8ce956c2010-08-17 11:13:53 -050019import com.android.common.speech.LoggingEvents;
20
Michael Kolb8233fac2010-10-26 16:08:53 -070021import android.app.Activity;
Grace Kloba22ac16e2009-10-07 18:00:23 -070022import android.app.AlertDialog;
Leon Scroggins58d56c62010-01-28 15:12:40 -050023import android.app.SearchManager;
Grace Kloba22ac16e2009-10-07 18:00:23 -070024import android.content.ContentResolver;
Grace Kloba22ac16e2009-10-07 18:00:23 -070025import android.content.DialogInterface;
Michael Kolbfe251992010-07-08 15:41:55 -070026import android.content.DialogInterface.OnCancelListener;
Jeff Hamilton8ce956c2010-08-17 11:13:53 -050027import android.content.Intent;
Leon Scroggins4cd97792010-12-03 15:31:56 -050028import android.database.Cursor;
29import android.database.sqlite.SQLiteException;
Grace Kloba22ac16e2009-10-07 18:00:23 -070030import android.graphics.Bitmap;
31import android.net.Uri;
32import android.net.http.SslError;
Leon Scroggins4cd97792010-12-03 15:31:56 -050033import android.os.AsyncTask;
Grace Kloba22ac16e2009-10-07 18:00:23 -070034import android.os.Bundle;
35import android.os.Message;
Kristian Monsen4dce3bf2010-02-02 13:37:09 +000036import android.os.SystemClock;
Leon Scroggins4cd97792010-12-03 15:31:56 -050037import android.provider.BrowserContract;
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -050038import android.speech.RecognizerResultsIntent;
Grace Kloba22ac16e2009-10-07 18:00:23 -070039import android.util.Log;
40import android.view.KeyEvent;
41import android.view.LayoutInflater;
42import android.view.View;
Grace Kloba50c241e2010-04-20 11:07:50 -070043import android.view.ViewStub;
Ben Murdochc42addf2010-01-28 15:19:59 +000044import android.webkit.ConsoleMessage;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -050045import android.webkit.DownloadListener;
Grace Kloba22ac16e2009-10-07 18:00:23 -070046import android.webkit.GeolocationPermissions;
47import android.webkit.HttpAuthHandler;
48import android.webkit.SslErrorHandler;
49import android.webkit.URLUtil;
50import android.webkit.ValueCallback;
51import android.webkit.WebBackForwardList;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -050052import android.webkit.WebBackForwardListClient;
Grace Kloba22ac16e2009-10-07 18:00:23 -070053import android.webkit.WebChromeClient;
54import android.webkit.WebHistoryItem;
Grace Kloba22ac16e2009-10-07 18:00:23 -070055import android.webkit.WebStorage;
56import android.webkit.WebView;
57import android.webkit.WebViewClient;
58import android.widget.FrameLayout;
Grace Kloba22ac16e2009-10-07 18:00:23 -070059import android.widget.LinearLayout;
60import android.widget.TextView;
Ben Murdoch8029a772010-11-16 11:58:21 +000061import android.widget.Toast;
Grace Kloba22ac16e2009-10-07 18:00:23 -070062
Michael Kolbfe251992010-07-08 15:41:55 -070063import java.util.ArrayList;
64import java.util.HashMap;
65import java.util.Iterator;
66import java.util.LinkedList;
67import java.util.Map;
68import java.util.Vector;
69
Grace Kloba22ac16e2009-10-07 18:00:23 -070070/**
71 * Class for maintaining Tabs with a main WebView and a subwindow.
72 */
73class Tab {
Michael Kolb8233fac2010-10-26 16:08:53 -070074
Grace Kloba22ac16e2009-10-07 18:00:23 -070075 // Log Tag
76 private static final String LOGTAG = "Tab";
Ben Murdochc42addf2010-01-28 15:19:59 +000077 // Special case the logtag for messages for the Console to make it easier to
78 // filter them and match the logtag used for these messages in older versions
79 // of the browser.
80 private static final String CONSOLE_LOGTAG = "browser";
81
Michael Kolb8233fac2010-10-26 16:08:53 -070082 final static int LOCK_ICON_UNSECURE = 0;
83 final static int LOCK_ICON_SECURE = 1;
84 final static int LOCK_ICON_MIXED = 2;
85
86 Activity mActivity;
87 private WebViewController mWebViewController;
88
Grace Kloba22ac16e2009-10-07 18:00:23 -070089 // The Geolocation permissions prompt
90 private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
91 // Main WebView wrapper
Michael Kolba713ec82010-11-29 17:27:06 -080092 private View mContainer;
Grace Kloba22ac16e2009-10-07 18:00:23 -070093 // Main WebView
94 private WebView mMainView;
95 // Subwindow container
96 private View mSubViewContainer;
97 // Subwindow WebView
98 private WebView mSubView;
99 // Saved bundle for when we are running low on memory. It contains the
100 // information needed to restore the WebView if the user goes back to the
101 // tab.
102 private Bundle mSavedState;
103 // Data used when displaying the tab in the picker.
104 private PickerData mPickerData;
105 // Parent Tab. This is the Tab that created this Tab, or null if the Tab was
106 // created by the UI
107 private Tab mParentTab;
108 // Tab that constructed by this Tab. This is used when this Tab is
109 // destroyed, it clears all mParentTab values in the children.
110 private Vector<Tab> mChildTabs;
111 // If true, the tab will be removed when back out of the first page.
112 private boolean mCloseOnExit;
113 // If true, the tab is in the foreground of the current activity.
114 private boolean mInForeground;
Michael Kolb8233fac2010-10-26 16:08:53 -0700115 // If true, the tab is in page loading state (after onPageStarted,
116 // before onPageFinsihed)
117 private boolean mInPageLoad;
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000118 // The time the load started, used to find load page time
119 private long mLoadStartTime;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700120 // Application identifier used to find tabs that another application wants
121 // to reuse.
122 private String mAppId;
123 // Keep the original url around to avoid killing the old WebView if the url
124 // has not changed.
125 private String mOriginalUrl;
Michael Kolb8233fac2010-10-26 16:08:53 -0700126 // Hold on to the currently loaded url
127 private String mCurrentUrl;
128 //The currently loaded title
129 private String mCurrentTitle;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700130 // Error console for the tab
131 private ErrorConsoleView mErrorConsole;
132 // the lock icon type and previous lock icon type for the tab
133 private int mLockIconType;
134 private int mPrevLockIconType;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -0500135 // The listener that gets invoked when a download is started from the
136 // mMainView
137 private final DownloadListener mDownloadListener;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500138 // Listener used to know when we move forward or back in the history list.
139 private final WebBackForwardListClient mWebBackForwardListClient;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700140
141 // AsyncTask for downloading touch icons
142 DownloadTouchIcon mTouchIconLoader;
143
144 // Extra saved information for displaying the tab in the picker.
145 private static class PickerData {
146 String mUrl;
147 String mTitle;
148 Bitmap mFavicon;
149 }
150
Leon Scroggins4cd97792010-12-03 15:31:56 -0500151 // Whether or not the currently shown page is a bookmarked site. Will be
152 // out of date when loading a new page until the mBookmarkAsyncTask returns.
153 private boolean mIsBookmarkedSite;
154 // Used to determine whether the current site is bookmarked.
155 private AsyncTask<Void, Void, Boolean> mBookmarkAsyncTask;
156
157 public boolean isBookmarkedSite() { return mIsBookmarkedSite; }
158
Grace Kloba22ac16e2009-10-07 18:00:23 -0700159 // Used for saving and restoring each Tab
160 static final String WEBVIEW = "webview";
161 static final String NUMTABS = "numTabs";
162 static final String CURRTAB = "currentTab";
163 static final String CURRURL = "currentUrl";
164 static final String CURRTITLE = "currentTitle";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700165 static final String CLOSEONEXIT = "closeonexit";
166 static final String PARENTTAB = "parentTab";
167 static final String APPID = "appid";
168 static final String ORIGINALURL = "originalUrl";
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700169 static final String INCOGNITO = "privateBrowsingEnabled";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700170
171 // -------------------------------------------------------------------------
172
Leon Scroggins58d56c62010-01-28 15:12:40 -0500173 /**
174 * Private information regarding the latest voice search. If the Tab is not
175 * in voice search mode, this will be null.
176 */
177 private VoiceSearchData mVoiceSearchData;
178 /**
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400179 * Remove voice search mode from this tab.
180 */
181 public void revertVoiceSearchMode() {
182 if (mVoiceSearchData != null) {
183 mVoiceSearchData = null;
184 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700185 mWebViewController.revertVoiceSearchMode(this);
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400186 }
187 }
188 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700189
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400190 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500191 * Return whether the tab is in voice search mode.
192 */
193 public boolean isInVoiceSearchMode() {
194 return mVoiceSearchData != null;
195 }
196 /**
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400197 * Return true if the Tab is in voice search mode and the voice search
198 * Intent came with a String identifying that Google provided the Intent.
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500199 */
200 public boolean voiceSearchSourceIsGoogle() {
201 return mVoiceSearchData != null && mVoiceSearchData.mSourceIsGoogle;
202 }
203 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500204 * Get the title to display for the current voice search page. If the Tab
205 * is not in voice search mode, return null.
206 */
207 public String getVoiceDisplayTitle() {
208 if (mVoiceSearchData == null) return null;
209 return mVoiceSearchData.mLastVoiceSearchTitle;
210 }
211 /**
212 * Get the latest array of voice search results, to be passed to the
213 * BrowserProvider. If the Tab is not in voice search mode, return null.
214 */
215 public ArrayList<String> getVoiceSearchResults() {
216 if (mVoiceSearchData == null) return null;
217 return mVoiceSearchData.mVoiceSearchResults;
218 }
219 /**
220 * Activate voice search mode.
221 * @param intent Intent which has the results to use, or an index into the
222 * results when reusing the old results.
223 */
224 /* package */ void activateVoiceSearchMode(Intent intent) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500225 int index = 0;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500226 ArrayList<String> results = intent.getStringArrayListExtra(
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500227 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_STRINGS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500228 if (results != null) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500229 ArrayList<String> urls = intent.getStringArrayListExtra(
230 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_URLS);
231 ArrayList<String> htmls = intent.getStringArrayListExtra(
232 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_HTML);
233 ArrayList<String> baseUrls = intent.getStringArrayListExtra(
234 RecognizerResultsIntent
235 .EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500236 // This tab is now entering voice search mode for the first time, or
237 // a new voice search was done.
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500238 int size = results.size();
239 if (urls == null || size != urls.size()) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500240 throw new AssertionError("improper extras passed in Intent");
241 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500242 if (htmls == null || htmls.size() != size || baseUrls == null ||
243 (baseUrls.size() != size && baseUrls.size() != 1)) {
244 // If either of these arrays are empty/incorrectly sized, ignore
245 // them.
246 htmls = null;
247 baseUrls = null;
248 }
249 mVoiceSearchData = new VoiceSearchData(results, urls, htmls,
250 baseUrls);
Leon Scroggins9df94972010-03-08 18:20:35 -0500251 mVoiceSearchData.mHeaders = intent.getParcelableArrayListExtra(
252 RecognizerResultsIntent
253 .EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS);
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500254 mVoiceSearchData.mSourceIsGoogle = intent.getBooleanExtra(
255 VoiceSearchData.SOURCE_IS_GOOGLE, false);
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400256 mVoiceSearchData.mVoiceSearchIntent = new Intent(intent);
Leon Scrogginse10dde52010-03-08 19:53:03 -0500257 }
258 String extraData = intent.getStringExtra(
259 SearchManager.EXTRA_DATA_KEY);
260 if (extraData != null) {
261 index = Integer.parseInt(extraData);
262 if (index >= mVoiceSearchData.mVoiceSearchResults.size()) {
263 throw new AssertionError("index must be less than "
264 + "size of mVoiceSearchResults");
265 }
266 if (mVoiceSearchData.mSourceIsGoogle) {
267 Intent logIntent = new Intent(
268 LoggingEvents.ACTION_LOG_EVENT);
269 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
270 LoggingEvents.VoiceSearch.N_BEST_CHOOSE);
271 logIntent.putExtra(
272 LoggingEvents.VoiceSearch.EXTRA_N_BEST_CHOOSE_INDEX,
273 index);
274 mActivity.sendBroadcast(logIntent);
275 }
276 if (mVoiceSearchData.mVoiceSearchIntent != null) {
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400277 // Copy the Intent, so that each history item will have its own
278 // Intent, with different (or none) extra data.
279 Intent latest = new Intent(mVoiceSearchData.mVoiceSearchIntent);
280 latest.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
281 mVoiceSearchData.mVoiceSearchIntent = latest;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500282 }
283 }
284 mVoiceSearchData.mLastVoiceSearchTitle
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500285 = mVoiceSearchData.mVoiceSearchResults.get(index);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500286 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700287 mWebViewController.activateVoiceSearchMode(mVoiceSearchData.mLastVoiceSearchTitle);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500288 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500289 if (mVoiceSearchData.mVoiceSearchHtmls != null) {
290 // When index was found it was already ensured that it was valid
291 String uriString = mVoiceSearchData.mVoiceSearchHtmls.get(index);
292 if (uriString != null) {
293 Uri dataUri = Uri.parse(uriString);
294 if (RecognizerResultsIntent.URI_SCHEME_INLINE.equals(
295 dataUri.getScheme())) {
296 // If there is only one base URL, use it. If there are
297 // more, there will be one for each index, so use the base
298 // URL corresponding to the index.
299 String baseUrl = mVoiceSearchData.mVoiceSearchBaseUrls.get(
300 mVoiceSearchData.mVoiceSearchBaseUrls.size() > 1 ?
301 index : 0);
302 mVoiceSearchData.mLastVoiceSearchUrl = baseUrl;
303 mMainView.loadDataWithBaseURL(baseUrl,
304 uriString.substring(RecognizerResultsIntent
305 .URI_SCHEME_INLINE.length() + 1), "text/html",
306 "utf-8", baseUrl);
307 return;
308 }
309 }
310 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500311 mVoiceSearchData.mLastVoiceSearchUrl
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500312 = mVoiceSearchData.mVoiceSearchUrls.get(index);
313 if (null == mVoiceSearchData.mLastVoiceSearchUrl) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700314 mVoiceSearchData.mLastVoiceSearchUrl = UrlUtils.smartUrlFilter(
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500315 mVoiceSearchData.mLastVoiceSearchTitle);
316 }
Leon Scroggins9df94972010-03-08 18:20:35 -0500317 Map<String, String> headers = null;
318 if (mVoiceSearchData.mHeaders != null) {
319 int bundleIndex = mVoiceSearchData.mHeaders.size() == 1 ? 0
320 : index;
321 Bundle bundle = mVoiceSearchData.mHeaders.get(bundleIndex);
322 if (bundle != null && !bundle.isEmpty()) {
323 Iterator<String> iter = bundle.keySet().iterator();
324 headers = new HashMap<String, String>();
325 while (iter.hasNext()) {
326 String key = iter.next();
327 headers.put(key, bundle.getString(key));
328 }
329 }
330 }
331 mMainView.loadUrl(mVoiceSearchData.mLastVoiceSearchUrl, headers);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500332 }
333 /* package */ static class VoiceSearchData {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500334 public VoiceSearchData(ArrayList<String> results,
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500335 ArrayList<String> urls, ArrayList<String> htmls,
336 ArrayList<String> baseUrls) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500337 mVoiceSearchResults = results;
338 mVoiceSearchUrls = urls;
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500339 mVoiceSearchHtmls = htmls;
340 mVoiceSearchBaseUrls = baseUrls;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500341 }
342 /*
343 * ArrayList of suggestions to be displayed when opening the
344 * SearchManager
345 */
346 public ArrayList<String> mVoiceSearchResults;
347 /*
348 * ArrayList of urls, associated with the suggestions in
349 * mVoiceSearchResults.
350 */
351 public ArrayList<String> mVoiceSearchUrls;
352 /*
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500353 * ArrayList holding content to load for each item in
354 * mVoiceSearchResults.
355 */
356 public ArrayList<String> mVoiceSearchHtmls;
357 /*
358 * ArrayList holding base urls for the items in mVoiceSearchResults.
359 * If non null, this will either have the same size as
360 * mVoiceSearchResults or have a size of 1, in which case all will use
361 * the same base url
362 */
363 public ArrayList<String> mVoiceSearchBaseUrls;
364 /*
Leon Scroggins58d56c62010-01-28 15:12:40 -0500365 * The last url provided by voice search. Used for comparison to see if
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500366 * we are going to a page by some method besides voice search.
Leon Scroggins58d56c62010-01-28 15:12:40 -0500367 */
368 public String mLastVoiceSearchUrl;
369 /**
370 * The last title used for voice search. Needed to update the title bar
371 * when switching tabs.
372 */
373 public String mLastVoiceSearchTitle;
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500374 /**
375 * Whether the Intent which turned on voice search mode contained the
376 * String signifying that Google was the source.
377 */
378 public boolean mSourceIsGoogle;
379 /**
Leon Scroggins9df94972010-03-08 18:20:35 -0500380 * List of headers to be passed into the WebView containing location
381 * information
382 */
383 public ArrayList<Bundle> mHeaders;
384 /**
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500385 * The Intent used to invoke voice search. Placed on the
386 * WebHistoryItem so that when coming back to a previous voice search
387 * page we can again activate voice search.
388 */
Leon Scrogginse10dde52010-03-08 19:53:03 -0500389 public Intent mVoiceSearchIntent;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500390 /**
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500391 * String used to identify Google as the source of voice search.
392 */
393 public static String SOURCE_IS_GOOGLE
394 = "android.speech.extras.SOURCE_IS_GOOGLE";
Leon Scroggins58d56c62010-01-28 15:12:40 -0500395 }
396
Grace Kloba22ac16e2009-10-07 18:00:23 -0700397 // Container class for the next error dialog that needs to be displayed
398 private class ErrorDialog {
399 public final int mTitle;
400 public final String mDescription;
401 public final int mError;
402 ErrorDialog(int title, String desc, int error) {
403 mTitle = title;
404 mDescription = desc;
405 mError = error;
406 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700407 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700408
409 private void processNextError() {
410 if (mQueuedErrors == null) {
411 return;
412 }
413 // The first one is currently displayed so just remove it.
414 mQueuedErrors.removeFirst();
415 if (mQueuedErrors.size() == 0) {
416 mQueuedErrors = null;
417 return;
418 }
419 showError(mQueuedErrors.getFirst());
420 }
421
422 private DialogInterface.OnDismissListener mDialogListener =
423 new DialogInterface.OnDismissListener() {
424 public void onDismiss(DialogInterface d) {
425 processNextError();
426 }
427 };
428 private LinkedList<ErrorDialog> mQueuedErrors;
429
430 private void queueError(int err, String desc) {
431 if (mQueuedErrors == null) {
432 mQueuedErrors = new LinkedList<ErrorDialog>();
433 }
434 for (ErrorDialog d : mQueuedErrors) {
435 if (d.mError == err) {
436 // Already saw a similar error, ignore the new one.
437 return;
438 }
439 }
440 ErrorDialog errDialog = new ErrorDialog(
441 err == WebViewClient.ERROR_FILE_NOT_FOUND ?
442 R.string.browserFrameFileErrorLabel :
443 R.string.browserFrameNetworkErrorLabel,
444 desc, err);
445 mQueuedErrors.addLast(errDialog);
446
447 // Show the dialog now if the queue was empty and it is in foreground
448 if (mQueuedErrors.size() == 1 && mInForeground) {
449 showError(errDialog);
450 }
451 }
452
453 private void showError(ErrorDialog errDialog) {
454 if (mInForeground) {
455 AlertDialog d = new AlertDialog.Builder(mActivity)
456 .setTitle(errDialog.mTitle)
457 .setMessage(errDialog.mDescription)
458 .setPositiveButton(R.string.ok, null)
459 .create();
460 d.setOnDismissListener(mDialogListener);
461 d.show();
462 }
463 }
464
465 // -------------------------------------------------------------------------
466 // WebViewClient implementation for the main WebView
467 // -------------------------------------------------------------------------
468
469 private final WebViewClient mWebViewClient = new WebViewClient() {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500470 private Message mDontResend;
471 private Message mResend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700472 @Override
473 public void onPageStarted(WebView view, String url, Bitmap favicon) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700474 mInPageLoad = true;
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000475 mLoadStartTime = SystemClock.uptimeMillis();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500476 if (mVoiceSearchData != null
477 && !url.equals(mVoiceSearchData.mLastVoiceSearchUrl)) {
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500478 if (mVoiceSearchData.mSourceIsGoogle) {
479 Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
480 i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
481 mActivity.sendBroadcast(i);
482 }
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400483 revertVoiceSearchMode();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500484 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700485
Grace Kloba22ac16e2009-10-07 18:00:23 -0700486
487 // If we start a touch icon load and then load a new page, we don't
488 // want to cancel the current touch icon loader. But, we do want to
489 // create a new one when the touch icon url is known.
490 if (mTouchIconLoader != null) {
491 mTouchIconLoader.mTab = null;
492 mTouchIconLoader = null;
493 }
494
495 // reset the error console
496 if (mErrorConsole != null) {
497 mErrorConsole.clearErrorMessages();
Michael Kolb8233fac2010-10-26 16:08:53 -0700498 if (mWebViewController.shouldShowErrorConsole()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700499 mErrorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
500 }
501 }
502
Grace Kloba22ac16e2009-10-07 18:00:23 -0700503 // finally update the UI in the activity if it is in the foreground
Michael Kolb8233fac2010-10-26 16:08:53 -0700504 mWebViewController.onPageStarted(Tab.this, view, url, favicon);
Leon Scroggins4cd97792010-12-03 15:31:56 -0500505
506 final String urlInQuestion = url;
507 if (mBookmarkAsyncTask != null) {
508 mBookmarkAsyncTask.cancel(true);
509 }
510 mBookmarkAsyncTask = new AsyncTask<Void, Void, Boolean>() {
511 @Override
512 protected Boolean doInBackground(Void... unused) {
513 // Check to see if the site is bookmarked
514 Cursor cursor = null;
515 try {
516 cursor = mActivity.getContentResolver().query(
517 BrowserContract.Bookmarks.CONTENT_URI,
518 new String[] { BrowserContract.Bookmarks.URL },
519 BrowserContract.Bookmarks.URL + " == ?",
520 new String[] { urlInQuestion },
521 null);
522 return cursor.moveToFirst();
523 } catch (SQLiteException e) {
524 Log.e(LOGTAG, "Error checking for bookmark: " + e);
525 return false;
526 } finally {
527 if (cursor != null) cursor.close();
528 }
529 }
530 @Override
531 protected void onPostExecute(Boolean isBookmarked) {
532 if (this == mBookmarkAsyncTask) {
533 mIsBookmarkedSite = isBookmarked;
534 mWebViewController.bookmarkedStatusHasChanged(Tab.this);
535 }
536 }
537 };
538 mBookmarkAsyncTask.execute();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700539 }
540
541 @Override
542 public void onPageFinished(WebView view, String url) {
John Reck5b691842010-11-29 11:21:13 -0800543 if (!isPrivateBrowsingEnabled()) {
544 LogTag.logPageFinishedLoading(
545 url, SystemClock.uptimeMillis() - mLoadStartTime);
546 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700547 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700548
Michael Kolb8233fac2010-10-26 16:08:53 -0700549 mWebViewController.onPageFinished(Tab.this, url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700550 }
551
552 // return true if want to hijack the url to let another app to handle it
553 @Override
554 public boolean shouldOverrideUrlLoading(WebView view, String url) {
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400555 if (voiceSearchSourceIsGoogle()) {
556 // This method is called when the user clicks on a link.
557 // VoiceSearchMode is turned off when the user leaves the
558 // Google results page, so at this point the user must be on
559 // that page. If the user clicked a link on that page, assume
560 // that the voice search was effective, and broadcast an Intent
561 // so a receiver can take note of that fact.
562 Intent logIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
563 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
564 LoggingEvents.VoiceSearch.RESULT_CLICKED);
565 mActivity.sendBroadcast(logIntent);
566 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700567 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700568 return mWebViewController.shouldOverrideUrlLoading(view, url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700569 } else {
570 return false;
571 }
572 }
573
574 /**
575 * Updates the lock icon. This method is called when we discover another
576 * resource to be loaded for this page (for example, javascript). While
577 * we update the icon type, we do not update the lock icon itself until
578 * we are done loading, it is slightly more secure this way.
579 */
580 @Override
581 public void onLoadResource(WebView view, String url) {
582 if (url != null && url.length() > 0) {
583 // It is only if the page claims to be secure that we may have
584 // to update the lock:
Michael Kolb8233fac2010-10-26 16:08:53 -0700585 if (mLockIconType == LOCK_ICON_SECURE) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700586 // If NOT a 'safe' url, change the lock to mixed content!
587 if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url)
588 || URLUtil.isAboutUrl(url))) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700589 mLockIconType = LOCK_ICON_MIXED;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700590 }
591 }
592 }
593 }
594
595 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -0700596 * Show a dialog informing the user of the network error reported by
597 * WebCore if it is in the foreground.
598 */
599 @Override
600 public void onReceivedError(WebView view, int errorCode,
601 String description, String failingUrl) {
602 if (errorCode != WebViewClient.ERROR_HOST_LOOKUP &&
603 errorCode != WebViewClient.ERROR_CONNECT &&
604 errorCode != WebViewClient.ERROR_BAD_URL &&
605 errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME &&
606 errorCode != WebViewClient.ERROR_FILE) {
607 queueError(errorCode, description);
608 }
Jeff Hamilton47654f42010-09-07 09:57:51 -0500609
610 // Don't log URLs when in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -0700611 if (!isPrivateBrowsingEnabled()) {
Jeff Hamilton47654f42010-09-07 09:57:51 -0500612 Log.e(LOGTAG, "onReceivedError " + errorCode + " " + failingUrl
613 + " " + description);
614 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700615
616 // We need to reset the title after an error if it is in foreground.
617 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700618 mWebViewController.resetTitleAndRevertLockIcon(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700619 }
620 }
621
622 /**
623 * Check with the user if it is ok to resend POST data as the page they
624 * are trying to navigate to is the result of a POST.
625 */
626 @Override
627 public void onFormResubmission(WebView view, final Message dontResend,
628 final Message resend) {
629 if (!mInForeground) {
630 dontResend.sendToTarget();
631 return;
632 }
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500633 if (mDontResend != null) {
634 Log.w(LOGTAG, "onFormResubmission should not be called again "
635 + "while dialog is still up");
636 dontResend.sendToTarget();
637 return;
638 }
639 mDontResend = dontResend;
640 mResend = resend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700641 new AlertDialog.Builder(mActivity).setTitle(
642 R.string.browserFrameFormResubmitLabel).setMessage(
643 R.string.browserFrameFormResubmitMessage)
644 .setPositiveButton(R.string.ok,
645 new DialogInterface.OnClickListener() {
646 public void onClick(DialogInterface dialog,
647 int which) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500648 if (mResend != null) {
649 mResend.sendToTarget();
650 mResend = null;
651 mDontResend = null;
652 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700653 }
654 }).setNegativeButton(R.string.cancel,
655 new DialogInterface.OnClickListener() {
656 public void onClick(DialogInterface dialog,
657 int which) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500658 if (mDontResend != null) {
659 mDontResend.sendToTarget();
660 mResend = null;
661 mDontResend = null;
662 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700663 }
664 }).setOnCancelListener(new OnCancelListener() {
665 public void onCancel(DialogInterface dialog) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500666 if (mDontResend != null) {
667 mDontResend.sendToTarget();
668 mResend = null;
669 mDontResend = null;
670 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700671 }
672 }).show();
673 }
674
675 /**
676 * Insert the url into the visited history database.
677 * @param url The url to be inserted.
678 * @param isReload True if this url is being reloaded.
679 * FIXME: Not sure what to do when reloading the page.
680 */
681 @Override
682 public void doUpdateVisitedHistory(WebView view, String url,
683 boolean isReload) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700684 mWebViewController.doUpdateVisitedHistory(Tab.this, url, isReload);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700685 }
686
687 /**
688 * Displays SSL error(s) dialog to the user.
689 */
690 @Override
691 public void onReceivedSslError(final WebView view,
692 final SslErrorHandler handler, final SslError error) {
693 if (!mInForeground) {
694 handler.cancel();
695 return;
696 }
697 if (BrowserSettings.getInstance().showSecurityWarnings()) {
698 final LayoutInflater factory =
699 LayoutInflater.from(mActivity);
700 final View warningsView =
701 factory.inflate(R.layout.ssl_warnings, null);
702 final LinearLayout placeholder =
703 (LinearLayout)warningsView.findViewById(R.id.placeholder);
704
705 if (error.hasError(SslError.SSL_UNTRUSTED)) {
706 LinearLayout ll = (LinearLayout)factory
707 .inflate(R.layout.ssl_warning, null);
708 ((TextView)ll.findViewById(R.id.warning))
709 .setText(R.string.ssl_untrusted);
710 placeholder.addView(ll);
711 }
712
713 if (error.hasError(SslError.SSL_IDMISMATCH)) {
714 LinearLayout ll = (LinearLayout)factory
715 .inflate(R.layout.ssl_warning, null);
716 ((TextView)ll.findViewById(R.id.warning))
717 .setText(R.string.ssl_mismatch);
718 placeholder.addView(ll);
719 }
720
721 if (error.hasError(SslError.SSL_EXPIRED)) {
722 LinearLayout ll = (LinearLayout)factory
723 .inflate(R.layout.ssl_warning, null);
724 ((TextView)ll.findViewById(R.id.warning))
725 .setText(R.string.ssl_expired);
726 placeholder.addView(ll);
727 }
728
729 if (error.hasError(SslError.SSL_NOTYETVALID)) {
730 LinearLayout ll = (LinearLayout)factory
731 .inflate(R.layout.ssl_warning, null);
732 ((TextView)ll.findViewById(R.id.warning))
733 .setText(R.string.ssl_not_yet_valid);
734 placeholder.addView(ll);
735 }
736
737 new AlertDialog.Builder(mActivity).setTitle(
738 R.string.security_warning).setIcon(
739 android.R.drawable.ic_dialog_alert).setView(
740 warningsView).setPositiveButton(R.string.ssl_continue,
741 new DialogInterface.OnClickListener() {
742 public void onClick(DialogInterface dialog,
743 int whichButton) {
744 handler.proceed();
745 }
746 }).setNeutralButton(R.string.view_certificate,
747 new DialogInterface.OnClickListener() {
748 public void onClick(DialogInterface dialog,
749 int whichButton) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700750 mWebViewController.showSslCertificateOnError(view,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700751 handler, error);
752 }
Ben Murdocha49b8292010-11-16 11:56:04 +0000753 }).setNegativeButton(R.string.ssl_go_back,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700754 new DialogInterface.OnClickListener() {
755 public void onClick(DialogInterface dialog,
756 int whichButton) {
757 handler.cancel();
Michael Kolb8233fac2010-10-26 16:08:53 -0700758 mWebViewController.resetTitleAndRevertLockIcon(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700759 }
760 }).setOnCancelListener(
761 new DialogInterface.OnCancelListener() {
762 public void onCancel(DialogInterface dialog) {
763 handler.cancel();
Michael Kolb8233fac2010-10-26 16:08:53 -0700764 mWebViewController.resetTitleAndRevertLockIcon(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700765 }
766 }).show();
767 } else {
768 handler.proceed();
769 }
770 }
771
772 /**
773 * Handles an HTTP authentication request.
774 *
775 * @param handler The authentication handler
776 * @param host The host
777 * @param realm The realm
778 */
779 @Override
780 public void onReceivedHttpAuthRequest(WebView view,
781 final HttpAuthHandler handler, final String host,
782 final String realm) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700783 mWebViewController.onReceivedHttpAuthRequest(Tab.this, view, handler, host, realm);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700784 }
785
786 @Override
787 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
788 if (!mInForeground) {
789 return false;
790 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700791 return mWebViewController.shouldOverrideKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700792 }
793
794 @Override
795 public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700796 if (!mInForeground) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700797 return;
798 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700799 mWebViewController.onUnhandledKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700800 }
801 };
802
803 // -------------------------------------------------------------------------
804 // WebChromeClient implementation for the main WebView
805 // -------------------------------------------------------------------------
806
807 private final WebChromeClient mWebChromeClient = new WebChromeClient() {
808 // Helper method to create a new tab or sub window.
809 private void createWindow(final boolean dialog, final Message msg) {
810 WebView.WebViewTransport transport =
811 (WebView.WebViewTransport) msg.obj;
812 if (dialog) {
813 createSubWindow();
Michael Kolb8233fac2010-10-26 16:08:53 -0700814 mWebViewController.attachSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700815 transport.setWebView(mSubView);
816 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -0700817 final Tab newTab = mWebViewController.openTabAndShow(
818 IntentHandler.EMPTY_URL_DATA, false, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700819 if (newTab != Tab.this) {
820 Tab.this.addChildTab(newTab);
821 }
822 transport.setWebView(newTab.getWebView());
823 }
824 msg.sendToTarget();
825 }
826
827 @Override
828 public boolean onCreateWindow(WebView view, final boolean dialog,
829 final boolean userGesture, final Message resultMsg) {
830 // only allow new window or sub window for the foreground case
831 if (!mInForeground) {
832 return false;
833 }
834 // Short-circuit if we can't create any more tabs or sub windows.
835 if (dialog && mSubView != null) {
836 new AlertDialog.Builder(mActivity)
837 .setTitle(R.string.too_many_subwindows_dialog_title)
838 .setIcon(android.R.drawable.ic_dialog_alert)
839 .setMessage(R.string.too_many_subwindows_dialog_message)
840 .setPositiveButton(R.string.ok, null)
841 .show();
842 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700843 } else if (!mWebViewController.getTabControl().canCreateNewTab()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700844 new AlertDialog.Builder(mActivity)
845 .setTitle(R.string.too_many_windows_dialog_title)
846 .setIcon(android.R.drawable.ic_dialog_alert)
847 .setMessage(R.string.too_many_windows_dialog_message)
848 .setPositiveButton(R.string.ok, null)
849 .show();
850 return false;
851 }
852
853 // Short-circuit if this was a user gesture.
854 if (userGesture) {
855 createWindow(dialog, resultMsg);
856 return true;
857 }
858
859 // Allow the popup and create the appropriate window.
860 final AlertDialog.OnClickListener allowListener =
861 new AlertDialog.OnClickListener() {
862 public void onClick(DialogInterface d,
863 int which) {
864 createWindow(dialog, resultMsg);
865 }
866 };
867
868 // Block the popup by returning a null WebView.
869 final AlertDialog.OnClickListener blockListener =
870 new AlertDialog.OnClickListener() {
871 public void onClick(DialogInterface d, int which) {
872 resultMsg.sendToTarget();
873 }
874 };
875
876 // Build a confirmation dialog to display to the user.
877 final AlertDialog d =
878 new AlertDialog.Builder(mActivity)
879 .setTitle(R.string.attention)
880 .setIcon(android.R.drawable.ic_dialog_alert)
881 .setMessage(R.string.popup_window_attempt)
882 .setPositiveButton(R.string.allow, allowListener)
883 .setNegativeButton(R.string.block, blockListener)
884 .setCancelable(false)
885 .create();
886
887 // Show the confirmation dialog.
888 d.show();
889 return true;
890 }
891
892 @Override
Patrick Scotteb5061b2009-11-18 15:00:30 -0500893 public void onRequestFocus(WebView view) {
894 if (!mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700895 mWebViewController.switchToTab(mWebViewController.getTabControl().getTabIndex(
Patrick Scotteb5061b2009-11-18 15:00:30 -0500896 Tab.this));
897 }
898 }
899
900 @Override
Grace Kloba22ac16e2009-10-07 18:00:23 -0700901 public void onCloseWindow(WebView window) {
902 if (mParentTab != null) {
903 // JavaScript can only close popup window.
904 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700905 mWebViewController.switchToTab(mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -0700906 .getTabIndex(mParentTab));
907 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700908 mWebViewController.closeTab(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700909 }
910 }
911
912 @Override
913 public void onProgressChanged(WebView view, int newProgress) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700914 mWebViewController.onProgressChanged(Tab.this, newProgress);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700915 }
916
917 @Override
Leon Scroggins21d9b902010-03-11 09:33:11 -0500918 public void onReceivedTitle(WebView view, final String title) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700919 mWebViewController.onReceivedTitle(Tab.this, title);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700920 }
921
922 @Override
923 public void onReceivedIcon(WebView view, Bitmap icon) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700924 mWebViewController.onFavicon(Tab.this, view, icon);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700925 }
926
927 @Override
928 public void onReceivedTouchIconUrl(WebView view, String url,
929 boolean precomposed) {
930 final ContentResolver cr = mActivity.getContentResolver();
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400931 // Let precomposed icons take precedence over non-composed
932 // icons.
933 if (precomposed && mTouchIconLoader != null) {
934 mTouchIconLoader.cancel(false);
935 mTouchIconLoader = null;
936 }
937 // Have only one async task at a time.
938 if (mTouchIconLoader == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700939 mTouchIconLoader = new DownloadTouchIcon(Tab.this,
940 mActivity, cr, view);
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400941 mTouchIconLoader.execute(url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700942 }
943 }
944
945 @Override
946 public void onShowCustomView(View view,
947 WebChromeClient.CustomViewCallback callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700948 if (mInForeground) mWebViewController.showCustomView(Tab.this, view,
949 callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700950 }
951
952 @Override
953 public void onHideCustomView() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700954 if (mInForeground) mWebViewController.hideCustomView();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700955 }
956
957 /**
958 * The origin has exceeded its database quota.
959 * @param url the URL that exceeded the quota
960 * @param databaseIdentifier the identifier of the database on which the
961 * transaction that caused the quota overflow was run
962 * @param currentQuota the current quota for the origin.
963 * @param estimatedSize the estimated size of the database.
964 * @param totalUsedQuota is the sum of all origins' quota.
965 * @param quotaUpdater The callback to run when a decision to allow or
966 * deny quota has been made. Don't forget to call this!
967 */
968 @Override
969 public void onExceededDatabaseQuota(String url,
970 String databaseIdentifier, long currentQuota, long estimatedSize,
971 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
972 BrowserSettings.getInstance().getWebStorageSizeManager()
973 .onExceededDatabaseQuota(url, databaseIdentifier,
974 currentQuota, estimatedSize, totalUsedQuota,
975 quotaUpdater);
976 }
977
978 /**
979 * The Application Cache has exceeded its max size.
980 * @param spaceNeeded is the amount of disk space that would be needed
981 * in order for the last appcache operation to succeed.
982 * @param totalUsedQuota is the sum of all origins' quota.
983 * @param quotaUpdater A callback to inform the WebCore thread that a
984 * new app cache size is available. This callback must always
985 * be executed at some point to ensure that the sleeping
986 * WebCore thread is woken up.
987 */
988 @Override
989 public void onReachedMaxAppCacheSize(long spaceNeeded,
990 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
991 BrowserSettings.getInstance().getWebStorageSizeManager()
992 .onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
993 quotaUpdater);
994 }
995
996 /**
997 * Instructs the browser to show a prompt to ask the user to set the
998 * Geolocation permission state for the specified origin.
999 * @param origin The origin for which Geolocation permissions are
1000 * requested.
1001 * @param callback The callback to call once the user has set the
1002 * Geolocation permission state.
1003 */
1004 @Override
1005 public void onGeolocationPermissionsShowPrompt(String origin,
1006 GeolocationPermissions.Callback callback) {
1007 if (mInForeground) {
Grace Kloba50c241e2010-04-20 11:07:50 -07001008 getGeolocationPermissionsPrompt().show(origin, callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001009 }
1010 }
1011
1012 /**
1013 * Instructs the browser to hide the Geolocation permissions prompt.
1014 */
1015 @Override
1016 public void onGeolocationPermissionsHidePrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001017 if (mInForeground && mGeolocationPermissionsPrompt != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001018 mGeolocationPermissionsPrompt.hide();
1019 }
1020 }
1021
Ben Murdoch65acc352009-11-19 18:16:04 +00001022 /* Adds a JavaScript error message to the system log and if the JS
1023 * console is enabled in the about:debug options, to that console
1024 * also.
Ben Murdochc42addf2010-01-28 15:19:59 +00001025 * @param consoleMessage the message object.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001026 */
1027 @Override
Ben Murdochc42addf2010-01-28 15:19:59 +00001028 public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001029 if (mInForeground) {
1030 // call getErrorConsole(true) so it will create one if needed
1031 ErrorConsoleView errorConsole = getErrorConsole(true);
Ben Murdochc42addf2010-01-28 15:19:59 +00001032 errorConsole.addErrorMessage(consoleMessage);
Michael Kolb8233fac2010-10-26 16:08:53 -07001033 if (mWebViewController.shouldShowErrorConsole()
1034 && errorConsole.getShowState() !=
1035 ErrorConsoleView.SHOW_MAXIMIZED) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001036 errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED);
1037 }
1038 }
Ben Murdochc42addf2010-01-28 15:19:59 +00001039
Jeff Hamilton47654f42010-09-07 09:57:51 -05001040 // Don't log console messages in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001041 if (isPrivateBrowsingEnabled()) return true;
Jeff Hamilton47654f42010-09-07 09:57:51 -05001042
Ben Murdochc42addf2010-01-28 15:19:59 +00001043 String message = "Console: " + consoleMessage.message() + " "
1044 + consoleMessage.sourceId() + ":"
1045 + consoleMessage.lineNumber();
1046
1047 switch (consoleMessage.messageLevel()) {
1048 case TIP:
1049 Log.v(CONSOLE_LOGTAG, message);
1050 break;
1051 case LOG:
1052 Log.i(CONSOLE_LOGTAG, message);
1053 break;
1054 case WARNING:
1055 Log.w(CONSOLE_LOGTAG, message);
1056 break;
1057 case ERROR:
1058 Log.e(CONSOLE_LOGTAG, message);
1059 break;
1060 case DEBUG:
1061 Log.d(CONSOLE_LOGTAG, message);
1062 break;
1063 }
1064
1065 return true;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001066 }
1067
1068 /**
1069 * Ask the browser for an icon to represent a <video> element.
1070 * This icon will be used if the Web page did not specify a poster attribute.
1071 * @return Bitmap The icon or null if no such icon is available.
1072 */
1073 @Override
1074 public Bitmap getDefaultVideoPoster() {
1075 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001076 return mWebViewController.getDefaultVideoPoster();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001077 }
1078 return null;
1079 }
1080
1081 /**
1082 * Ask the host application for a custom progress view to show while
1083 * a <video> is loading.
1084 * @return View The progress view.
1085 */
1086 @Override
1087 public View getVideoLoadingProgressView() {
1088 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001089 return mWebViewController.getVideoLoadingProgressView();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001090 }
1091 return null;
1092 }
1093
1094 @Override
Ben Murdoch62b1b7e2010-05-19 20:38:56 +01001095 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001096 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001097 mWebViewController.openFileChooser(uploadMsg, acceptType);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001098 } else {
1099 uploadMsg.onReceiveValue(null);
1100 }
1101 }
1102
1103 /**
1104 * Deliver a list of already-visited URLs
1105 */
1106 @Override
1107 public void getVisitedHistory(final ValueCallback<String[]> callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001108 mWebViewController.getVisitedHistory(callback);
1109 }
Ben Murdoch8029a772010-11-16 11:58:21 +00001110
1111 @Override
1112 public void setupAutoFill(Message message) {
1113 // Prompt the user to set up their profile.
1114 final Message msg = message;
1115 AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
1116 builder.setMessage(R.string.autofill_setup_dialog_message)
1117 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
1118 @Override
1119 public void onClick(DialogInterface dialog, int id) {
1120 // Take user to the AutoFill profile editor. When they return,
1121 // we will send the message that we pass here which will trigger
1122 // the form to get filled out with their new profile.
1123 mWebViewController.setupAutoFill(msg);
1124 }
1125 })
1126 .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
1127 @Override
1128 public void onClick(DialogInterface dialog, int id) {
1129 // Disable autofill and show a toast with how to turn it on again.
1130 BrowserSettings s = BrowserSettings.getInstance();
1131 s.addObserver(mMainView.getSettings());
1132 s.disableAutoFill(mActivity);
1133 s.update();
1134 Toast.makeText(mActivity, R.string.autofill_setup_dialog_negative_toast,
1135 Toast.LENGTH_LONG).show();
1136 }
1137 }).show();
1138 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001139 };
1140
1141 // -------------------------------------------------------------------------
1142 // WebViewClient implementation for the sub window
1143 // -------------------------------------------------------------------------
1144
1145 // Subclass of WebViewClient used in subwindows to notify the main
1146 // WebViewClient of certain WebView activities.
1147 private static class SubWindowClient extends WebViewClient {
1148 // The main WebViewClient.
1149 private final WebViewClient mClient;
Michael Kolb8233fac2010-10-26 16:08:53 -07001150 private final WebViewController mController;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001151
Michael Kolb8233fac2010-10-26 16:08:53 -07001152 SubWindowClient(WebViewClient client, WebViewController controller) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001153 mClient = client;
Michael Kolb8233fac2010-10-26 16:08:53 -07001154 mController = controller;
Leon Scroggins III211ba542010-04-19 13:21:13 -04001155 }
1156 @Override
1157 public void onPageStarted(WebView view, String url, Bitmap favicon) {
1158 // Unlike the others, do not call mClient's version, which would
1159 // change the progress bar. However, we do want to remove the
Cary Clark01cfcdd2010-06-04 16:36:45 -04001160 // find or select dialog.
Michael Kolb8233fac2010-10-26 16:08:53 -07001161 mController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001162 }
1163 @Override
1164 public void doUpdateVisitedHistory(WebView view, String url,
1165 boolean isReload) {
1166 mClient.doUpdateVisitedHistory(view, url, isReload);
1167 }
1168 @Override
1169 public boolean shouldOverrideUrlLoading(WebView view, String url) {
1170 return mClient.shouldOverrideUrlLoading(view, url);
1171 }
1172 @Override
1173 public void onReceivedSslError(WebView view, SslErrorHandler handler,
1174 SslError error) {
1175 mClient.onReceivedSslError(view, handler, error);
1176 }
1177 @Override
1178 public void onReceivedHttpAuthRequest(WebView view,
1179 HttpAuthHandler handler, String host, String realm) {
1180 mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
1181 }
1182 @Override
1183 public void onFormResubmission(WebView view, Message dontResend,
1184 Message resend) {
1185 mClient.onFormResubmission(view, dontResend, resend);
1186 }
1187 @Override
1188 public void onReceivedError(WebView view, int errorCode,
1189 String description, String failingUrl) {
1190 mClient.onReceivedError(view, errorCode, description, failingUrl);
1191 }
1192 @Override
1193 public boolean shouldOverrideKeyEvent(WebView view,
1194 android.view.KeyEvent event) {
1195 return mClient.shouldOverrideKeyEvent(view, event);
1196 }
1197 @Override
1198 public void onUnhandledKeyEvent(WebView view,
1199 android.view.KeyEvent event) {
1200 mClient.onUnhandledKeyEvent(view, event);
1201 }
1202 }
1203
1204 // -------------------------------------------------------------------------
1205 // WebChromeClient implementation for the sub window
1206 // -------------------------------------------------------------------------
1207
1208 private class SubWindowChromeClient extends WebChromeClient {
1209 // The main WebChromeClient.
1210 private final WebChromeClient mClient;
1211
1212 SubWindowChromeClient(WebChromeClient client) {
1213 mClient = client;
1214 }
1215 @Override
1216 public void onProgressChanged(WebView view, int newProgress) {
1217 mClient.onProgressChanged(view, newProgress);
1218 }
1219 @Override
1220 public boolean onCreateWindow(WebView view, boolean dialog,
1221 boolean userGesture, android.os.Message resultMsg) {
1222 return mClient.onCreateWindow(view, dialog, userGesture, resultMsg);
1223 }
1224 @Override
1225 public void onCloseWindow(WebView window) {
1226 if (window != mSubView) {
1227 Log.e(LOGTAG, "Can't close the window");
1228 }
Michael Kolb8233fac2010-10-26 16:08:53 -07001229 mWebViewController.dismissSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001230 }
1231 }
1232
1233 // -------------------------------------------------------------------------
1234
Michael Kolb8233fac2010-10-26 16:08:53 -07001235 // TODO temporarily use activity here
1236 // remove later
1237
Grace Kloba22ac16e2009-10-07 18:00:23 -07001238 // Construct a new tab
Michael Kolb8233fac2010-10-26 16:08:53 -07001239 Tab(WebViewController wvcontroller, WebView w, boolean closeOnExit, String appId,
Grace Kloba22ac16e2009-10-07 18:00:23 -07001240 String url) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001241 mWebViewController = wvcontroller;
1242 mActivity = mWebViewController.getActivity();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001243 mCloseOnExit = closeOnExit;
1244 mAppId = appId;
1245 mOriginalUrl = url;
Michael Kolb8233fac2010-10-26 16:08:53 -07001246 mLockIconType = LOCK_ICON_UNSECURE;
1247 mPrevLockIconType = LOCK_ICON_UNSECURE;
1248 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001249 mInForeground = false;
1250
Grace Kloba22ac16e2009-10-07 18:00:23 -07001251
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001252 mDownloadListener = new DownloadListener() {
1253 public void onDownloadStart(String url, String userAgent,
1254 String contentDisposition, String mimetype,
1255 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001256 mWebViewController.onDownloadStart(Tab.this, url, userAgent, contentDisposition,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001257 mimetype, contentLength);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001258 }
1259 };
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001260 mWebBackForwardListClient = new WebBackForwardListClient() {
1261 @Override
1262 public void onNewHistoryItem(WebHistoryItem item) {
1263 if (isInVoiceSearchMode()) {
1264 item.setCustomData(mVoiceSearchData.mVoiceSearchIntent);
1265 }
1266 }
1267 @Override
1268 public void onIndexChanged(WebHistoryItem item, int index) {
1269 Object data = item.getCustomData();
1270 if (data != null && data instanceof Intent) {
1271 activateVoiceSearchMode((Intent) data);
1272 }
1273 }
1274 };
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001275
Grace Kloba22ac16e2009-10-07 18:00:23 -07001276 setWebView(w);
1277 }
1278
1279 /**
1280 * Sets the WebView for this tab, correctly removing the old WebView from
1281 * the container view.
1282 */
1283 void setWebView(WebView w) {
1284 if (mMainView == w) {
1285 return;
1286 }
Michael Kolba713ec82010-11-29 17:27:06 -08001287
Grace Kloba22ac16e2009-10-07 18:00:23 -07001288 // If the WebView is changing, the page will be reloaded, so any ongoing
1289 // Geolocation permission requests are void.
Grace Kloba50c241e2010-04-20 11:07:50 -07001290 if (mGeolocationPermissionsPrompt != null) {
1291 mGeolocationPermissionsPrompt.hide();
1292 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001293
Michael Kolba713ec82010-11-29 17:27:06 -08001294 mWebViewController.onSetWebView(this, w);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001295
1296 // set the new one
1297 mMainView = w;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001298 // attach the WebViewClient, WebChromeClient and DownloadListener
Grace Kloba22ac16e2009-10-07 18:00:23 -07001299 if (mMainView != null) {
1300 mMainView.setWebViewClient(mWebViewClient);
1301 mMainView.setWebChromeClient(mWebChromeClient);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001302 // Attach DownloadManager so that downloads can start in an active
1303 // or a non-active window. This can happen when going to a site that
1304 // does a redirect after a period of time. The user could have
1305 // switched to another tab while waiting for the download to start.
1306 mMainView.setDownloadListener(mDownloadListener);
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001307 mMainView.setWebBackForwardListClient(mWebBackForwardListClient);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001308 }
1309 }
1310
1311 /**
1312 * Destroy the tab's main WebView and subWindow if any
1313 */
1314 void destroy() {
1315 if (mMainView != null) {
1316 dismissSubWindow();
1317 BrowserSettings.getInstance().deleteObserver(mMainView.getSettings());
1318 // save the WebView to call destroy() after detach it from the tab
1319 WebView webView = mMainView;
1320 setWebView(null);
1321 webView.destroy();
1322 }
1323 }
1324
1325 /**
1326 * Remove the tab from the parent
1327 */
1328 void removeFromTree() {
1329 // detach the children
1330 if (mChildTabs != null) {
1331 for(Tab t : mChildTabs) {
1332 t.setParentTab(null);
1333 }
1334 }
1335 // remove itself from the parent list
1336 if (mParentTab != null) {
1337 mParentTab.mChildTabs.remove(this);
1338 }
1339 }
1340
1341 /**
1342 * Create a new subwindow unless a subwindow already exists.
1343 * @return True if a new subwindow was created. False if one already exists.
1344 */
1345 boolean createSubWindow() {
1346 if (mSubView == null) {
Michael Kolb1514bb72010-11-22 09:11:48 -08001347 mWebViewController.createSubWindow(this);
Leon Scroggins III211ba542010-04-19 13:21:13 -04001348 mSubView.setWebViewClient(new SubWindowClient(mWebViewClient,
Michael Kolb8233fac2010-10-26 16:08:53 -07001349 mWebViewController));
Grace Kloba22ac16e2009-10-07 18:00:23 -07001350 mSubView.setWebChromeClient(new SubWindowChromeClient(
1351 mWebChromeClient));
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001352 // Set a different DownloadListener for the mSubView, since it will
1353 // just need to dismiss the mSubView, rather than close the Tab
1354 mSubView.setDownloadListener(new DownloadListener() {
1355 public void onDownloadStart(String url, String userAgent,
1356 String contentDisposition, String mimetype,
1357 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001358 mWebViewController.onDownloadStart(Tab.this, url, userAgent,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001359 contentDisposition, mimetype, contentLength);
1360 if (mSubView.copyBackForwardList().getSize() == 0) {
1361 // This subwindow was opened for the sole purpose of
1362 // downloading a file. Remove it.
Michael Kolb8233fac2010-10-26 16:08:53 -07001363 mWebViewController.dismissSubWindow(Tab.this);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001364 }
1365 }
1366 });
Grace Kloba22ac16e2009-10-07 18:00:23 -07001367 mSubView.setOnCreateContextMenuListener(mActivity);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001368 return true;
1369 }
1370 return false;
1371 }
1372
1373 /**
1374 * Dismiss the subWindow for the tab.
1375 */
1376 void dismissSubWindow() {
1377 if (mSubView != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001378 mWebViewController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001379 BrowserSettings.getInstance().deleteObserver(
1380 mSubView.getSettings());
1381 mSubView.destroy();
1382 mSubView = null;
1383 mSubViewContainer = null;
1384 }
1385 }
1386
Grace Kloba22ac16e2009-10-07 18:00:23 -07001387
1388 /**
1389 * Set the parent tab of this tab.
1390 */
1391 void setParentTab(Tab parent) {
1392 mParentTab = parent;
1393 // This tab may have been freed due to low memory. If that is the case,
1394 // the parent tab index is already saved. If we are changing that index
1395 // (most likely due to removing the parent tab) we must update the
1396 // parent tab index in the saved Bundle.
1397 if (mSavedState != null) {
1398 if (parent == null) {
1399 mSavedState.remove(PARENTTAB);
1400 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -07001401 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -07001402 .getTabIndex(parent));
1403 }
1404 }
1405 }
1406
1407 /**
1408 * When a Tab is created through the content of another Tab, then we
1409 * associate the Tabs.
1410 * @param child the Tab that was created from this Tab
1411 */
1412 void addChildTab(Tab child) {
1413 if (mChildTabs == null) {
1414 mChildTabs = new Vector<Tab>();
1415 }
1416 mChildTabs.add(child);
1417 child.setParentTab(this);
1418 }
1419
1420 Vector<Tab> getChildTabs() {
1421 return mChildTabs;
1422 }
1423
1424 void resume() {
1425 if (mMainView != null) {
1426 mMainView.onResume();
1427 if (mSubView != null) {
1428 mSubView.onResume();
1429 }
1430 }
1431 }
1432
1433 void pause() {
1434 if (mMainView != null) {
1435 mMainView.onPause();
1436 if (mSubView != null) {
1437 mSubView.onPause();
1438 }
1439 }
1440 }
1441
1442 void putInForeground() {
1443 mInForeground = true;
1444 resume();
1445 mMainView.setOnCreateContextMenuListener(mActivity);
1446 if (mSubView != null) {
1447 mSubView.setOnCreateContextMenuListener(mActivity);
1448 }
1449 // Show the pending error dialog if the queue is not empty
1450 if (mQueuedErrors != null && mQueuedErrors.size() > 0) {
1451 showError(mQueuedErrors.getFirst());
1452 }
1453 }
1454
1455 void putInBackground() {
1456 mInForeground = false;
1457 pause();
1458 mMainView.setOnCreateContextMenuListener(null);
1459 if (mSubView != null) {
1460 mSubView.setOnCreateContextMenuListener(null);
1461 }
1462 }
1463
Michael Kolb8233fac2010-10-26 16:08:53 -07001464 boolean inForeground() {
1465 return mInForeground;
1466 }
1467
Grace Kloba22ac16e2009-10-07 18:00:23 -07001468 /**
1469 * Return the top window of this tab; either the subwindow if it is not
1470 * null or the main window.
1471 * @return The top window of this tab.
1472 */
1473 WebView getTopWindow() {
1474 if (mSubView != null) {
1475 return mSubView;
1476 }
1477 return mMainView;
1478 }
1479
1480 /**
1481 * Return the main window of this tab. Note: if a tab is freed in the
1482 * background, this can return null. It is only guaranteed to be
1483 * non-null for the current tab.
1484 * @return The main WebView of this tab.
1485 */
1486 WebView getWebView() {
1487 return mMainView;
1488 }
1489
Michael Kolba713ec82010-11-29 17:27:06 -08001490 void setViewContainer(View container) {
1491 mContainer = container;
1492 }
1493
Michael Kolb8233fac2010-10-26 16:08:53 -07001494 View getViewContainer() {
1495 return mContainer;
1496 }
1497
Grace Kloba22ac16e2009-10-07 18:00:23 -07001498 /**
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001499 * Return whether private browsing is enabled for the main window of
1500 * this tab.
1501 * @return True if private browsing is enabled.
1502 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001503 boolean isPrivateBrowsingEnabled() {
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001504 WebView webView = getWebView();
1505 if (webView == null) {
1506 return false;
1507 }
1508 return webView.isPrivateBrowsingEnabled();
1509 }
1510
1511 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -07001512 * Return the subwindow of this tab or null if there is no subwindow.
1513 * @return The subwindow of this tab or null.
1514 */
1515 WebView getSubWebView() {
1516 return mSubView;
1517 }
1518
Michael Kolb1514bb72010-11-22 09:11:48 -08001519 void setSubWebView(WebView subView) {
1520 mSubView = subView;
1521 }
1522
Michael Kolb8233fac2010-10-26 16:08:53 -07001523 View getSubViewContainer() {
1524 return mSubViewContainer;
1525 }
1526
Michael Kolb1514bb72010-11-22 09:11:48 -08001527 void setSubViewContainer(View subViewContainer) {
1528 mSubViewContainer = subViewContainer;
1529 }
1530
Grace Kloba22ac16e2009-10-07 18:00:23 -07001531 /**
1532 * @return The geolocation permissions prompt for this tab.
1533 */
1534 GeolocationPermissionsPrompt getGeolocationPermissionsPrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001535 if (mGeolocationPermissionsPrompt == null) {
1536 ViewStub stub = (ViewStub) mContainer
1537 .findViewById(R.id.geolocation_permissions_prompt);
1538 mGeolocationPermissionsPrompt = (GeolocationPermissionsPrompt) stub
1539 .inflate();
1540 mGeolocationPermissionsPrompt.init();
1541 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001542 return mGeolocationPermissionsPrompt;
1543 }
1544
1545 /**
1546 * @return The application id string
1547 */
1548 String getAppId() {
1549 return mAppId;
1550 }
1551
1552 /**
1553 * Set the application id string
1554 * @param id
1555 */
1556 void setAppId(String id) {
1557 mAppId = id;
1558 }
1559
1560 /**
1561 * @return The original url associated with this Tab
1562 */
1563 String getOriginalUrl() {
1564 return mOriginalUrl;
1565 }
1566
1567 /**
1568 * Set the original url associated with this tab
1569 */
1570 void setOriginalUrl(String url) {
1571 mOriginalUrl = url;
1572 }
1573
1574 /**
Michael Kolb8233fac2010-10-26 16:08:53 -07001575 * set the title for the tab
1576 */
1577 void setCurrentTitle(String title) {
1578 mCurrentTitle = title;
1579 }
1580
1581 /**
1582 * set url for this tab
1583 * @param url
1584 */
1585 void setCurrentUrl(String url) {
1586 mCurrentUrl = url;
1587 }
1588
1589 String getCurrentTitle() {
1590 return mCurrentTitle;
1591 }
1592
1593 String getCurrentUrl() {
1594 return mCurrentUrl;
1595 }
1596 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -07001597 * Get the url of this tab. Valid after calling populatePickerData, but
1598 * before calling wipePickerData, or if the webview has been destroyed.
1599 * @return The WebView's url or null.
1600 */
1601 String getUrl() {
1602 if (mPickerData != null) {
1603 return mPickerData.mUrl;
1604 }
1605 return null;
1606 }
1607
1608 /**
1609 * Get the title of this tab. Valid after calling populatePickerData, but
1610 * before calling wipePickerData, or if the webview has been destroyed. If
1611 * the url has no title, use the url instead.
1612 * @return The WebView's title (or url) or null.
1613 */
1614 String getTitle() {
1615 if (mPickerData != null) {
1616 return mPickerData.mTitle;
1617 }
1618 return null;
1619 }
1620
1621 /**
1622 * Get the favicon of this tab. Valid after calling populatePickerData, but
1623 * before calling wipePickerData, or if the webview has been destroyed.
1624 * @return The WebView's favicon or null.
1625 */
1626 Bitmap getFavicon() {
1627 if (mPickerData != null) {
1628 return mPickerData.mFavicon;
1629 }
1630 return null;
1631 }
1632
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001633
Grace Kloba22ac16e2009-10-07 18:00:23 -07001634 /**
1635 * Return the tab's error console. Creates the console if createIfNEcessary
1636 * is true and we haven't already created the console.
1637 * @param createIfNecessary Flag to indicate if the console should be
1638 * created if it has not been already.
1639 * @return The tab's error console, or null if one has not been created and
1640 * createIfNecessary is false.
1641 */
1642 ErrorConsoleView getErrorConsole(boolean createIfNecessary) {
1643 if (createIfNecessary && mErrorConsole == null) {
1644 mErrorConsole = new ErrorConsoleView(mActivity);
1645 mErrorConsole.setWebView(mMainView);
1646 }
1647 return mErrorConsole;
1648 }
1649
1650 /**
1651 * If this Tab was created through another Tab, then this method returns
1652 * that Tab.
1653 * @return the Tab parent or null
1654 */
1655 public Tab getParentTab() {
1656 return mParentTab;
1657 }
1658
1659 /**
1660 * Return whether this tab should be closed when it is backing out of the
1661 * first page.
1662 * @return TRUE if this tab should be closed when exit.
1663 */
1664 boolean closeOnExit() {
1665 return mCloseOnExit;
1666 }
1667
1668 /**
1669 * Saves the current lock-icon state before resetting the lock icon. If we
1670 * have an error, we may need to roll back to the previous state.
1671 */
1672 void resetLockIcon(String url) {
1673 mPrevLockIconType = mLockIconType;
Michael Kolb8233fac2010-10-26 16:08:53 -07001674 mLockIconType = LOCK_ICON_UNSECURE;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001675 if (URLUtil.isHttpsUrl(url)) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001676 mLockIconType = LOCK_ICON_SECURE;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001677 }
1678 }
1679
1680 /**
1681 * Reverts the lock-icon state to the last saved state, for example, if we
1682 * had an error, and need to cancel the load.
1683 */
1684 void revertLockIcon() {
1685 mLockIconType = mPrevLockIconType;
1686 }
1687
1688 /**
1689 * @return The tab's lock icon type.
1690 */
1691 int getLockIconType() {
1692 return mLockIconType;
1693 }
1694
1695 /**
1696 * @return TRUE if onPageStarted is called while onPageFinished is not
1697 * called yet.
1698 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001699 boolean inPageLoad() {
1700 return mInPageLoad;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001701 }
1702
1703 // force mInLoad to be false. This should only be called before closing the
1704 // tab to ensure BrowserActivity's pauseWebViewTimers() is called correctly.
Michael Kolb8233fac2010-10-26 16:08:53 -07001705 void clearInPageLoad() {
1706 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001707 }
1708
1709 void populatePickerData() {
1710 if (mMainView == null) {
1711 populatePickerDataFromSavedState();
1712 return;
1713 }
1714
1715 // FIXME: The only place we cared about subwindow was for
1716 // bookmarking (i.e. not when saving state). Was this deliberate?
1717 final WebBackForwardList list = mMainView.copyBackForwardList();
Leon Scroggins70a153b2010-05-10 17:27:26 -04001718 if (list == null) {
1719 Log.w(LOGTAG, "populatePickerData called and WebBackForwardList is null");
1720 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001721 final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
1722 populatePickerData(item);
1723 }
1724
1725 // Populate the picker data using the given history item and the current top
1726 // WebView.
1727 private void populatePickerData(WebHistoryItem item) {
1728 mPickerData = new PickerData();
Leon Scroggins70a153b2010-05-10 17:27:26 -04001729 if (item == null) {
1730 Log.w(LOGTAG, "populatePickerData called with a null WebHistoryItem");
1731 } else {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001732 mPickerData.mUrl = item.getUrl();
1733 mPickerData.mTitle = item.getTitle();
1734 mPickerData.mFavicon = item.getFavicon();
1735 if (mPickerData.mTitle == null) {
1736 mPickerData.mTitle = mPickerData.mUrl;
1737 }
1738 }
1739 }
1740
1741 // Create the PickerData and populate it using the saved state of the tab.
1742 void populatePickerDataFromSavedState() {
1743 if (mSavedState == null) {
1744 return;
1745 }
1746 mPickerData = new PickerData();
1747 mPickerData.mUrl = mSavedState.getString(CURRURL);
1748 mPickerData.mTitle = mSavedState.getString(CURRTITLE);
1749 }
1750
1751 void clearPickerData() {
1752 mPickerData = null;
1753 }
1754
1755 /**
1756 * Get the saved state bundle.
1757 * @return
1758 */
1759 Bundle getSavedState() {
1760 return mSavedState;
1761 }
1762
1763 /**
1764 * Set the saved state.
1765 */
1766 void setSavedState(Bundle state) {
1767 mSavedState = state;
1768 }
1769
1770 /**
1771 * @return TRUE if succeed in saving the state.
1772 */
1773 boolean saveState() {
1774 // If the WebView is null it means we ran low on memory and we already
1775 // stored the saved state in mSavedState.
1776 if (mMainView == null) {
1777 return mSavedState != null;
1778 }
1779
1780 mSavedState = new Bundle();
1781 final WebBackForwardList list = mMainView.saveState(mSavedState);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001782
1783 // Store some extra info for displaying the tab in the picker.
1784 final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
1785 populatePickerData(item);
1786
1787 if (mPickerData.mUrl != null) {
1788 mSavedState.putString(CURRURL, mPickerData.mUrl);
1789 }
1790 if (mPickerData.mTitle != null) {
1791 mSavedState.putString(CURRTITLE, mPickerData.mTitle);
1792 }
1793 mSavedState.putBoolean(CLOSEONEXIT, mCloseOnExit);
1794 if (mAppId != null) {
1795 mSavedState.putString(APPID, mAppId);
1796 }
1797 if (mOriginalUrl != null) {
1798 mSavedState.putString(ORIGINALURL, mOriginalUrl);
1799 }
1800 // Remember the parent tab so the relationship can be restored.
1801 if (mParentTab != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001802 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl().getTabIndex(
Grace Kloba22ac16e2009-10-07 18:00:23 -07001803 mParentTab));
1804 }
1805 return true;
1806 }
1807
1808 /*
1809 * Restore the state of the tab.
1810 */
1811 boolean restoreState(Bundle b) {
1812 if (b == null) {
1813 return false;
1814 }
1815 // Restore the internal state even if the WebView fails to restore.
1816 // This will maintain the app id, original url and close-on-exit values.
1817 mSavedState = null;
1818 mPickerData = null;
1819 mCloseOnExit = b.getBoolean(CLOSEONEXIT);
1820 mAppId = b.getString(APPID);
1821 mOriginalUrl = b.getString(ORIGINALURL);
1822
1823 final WebBackForwardList list = mMainView.restoreState(b);
1824 if (list == null) {
1825 return false;
1826 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001827 return true;
1828 }
Leon Scroggins III211ba542010-04-19 13:21:13 -04001829
Grace Kloba22ac16e2009-10-07 18:00:23 -07001830}