blob: f43a68bf43c8b9d08566c727891664d088b88e53 [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
John Reck438bf462011-01-12 18:11:46 -080019import com.android.browser.homepages.HomeProvider;
Jeff Hamilton8ce956c2010-08-17 11:13:53 -050020import com.android.common.speech.LoggingEvents;
21
Michael Kolb8233fac2010-10-26 16:08:53 -070022import android.app.Activity;
Grace Kloba22ac16e2009-10-07 18:00:23 -070023import android.app.AlertDialog;
Leon Scroggins58d56c62010-01-28 15:12:40 -050024import android.app.SearchManager;
Grace Kloba22ac16e2009-10-07 18:00:23 -070025import android.content.ContentResolver;
John Reck30c714c2010-12-16 17:30:34 -080026import android.content.Context;
Grace Kloba22ac16e2009-10-07 18:00:23 -070027import android.content.DialogInterface;
Michael Kolbfe251992010-07-08 15:41:55 -070028import android.content.DialogInterface.OnCancelListener;
Jeff Hamilton8ce956c2010-08-17 11:13:53 -050029import android.content.Intent;
Grace Kloba22ac16e2009-10-07 18:00:23 -070030import android.graphics.Bitmap;
John Reck30c714c2010-12-16 17:30:34 -080031import android.graphics.BitmapFactory;
Grace Kloba22ac16e2009-10-07 18:00:23 -070032import android.net.Uri;
33import android.net.http.SslError;
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 Scrogginsa1cc3fd2010-02-01 16:14:11 -050037import android.speech.RecognizerResultsIntent;
Grace Kloba22ac16e2009-10-07 18:00:23 -070038import android.util.Log;
39import android.view.KeyEvent;
40import android.view.LayoutInflater;
41import android.view.View;
Grace Kloba50c241e2010-04-20 11:07:50 -070042import android.view.ViewStub;
Ben Murdochc42addf2010-01-28 15:19:59 +000043import android.webkit.ConsoleMessage;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -050044import android.webkit.DownloadListener;
Grace Kloba22ac16e2009-10-07 18:00:23 -070045import android.webkit.GeolocationPermissions;
46import android.webkit.HttpAuthHandler;
47import android.webkit.SslErrorHandler;
48import android.webkit.URLUtil;
49import android.webkit.ValueCallback;
50import android.webkit.WebBackForwardList;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -050051import android.webkit.WebBackForwardListClient;
Grace Kloba22ac16e2009-10-07 18:00:23 -070052import android.webkit.WebChromeClient;
53import android.webkit.WebHistoryItem;
John Reck438bf462011-01-12 18:11:46 -080054import android.webkit.WebResourceResponse;
Grace Kloba22ac16e2009-10-07 18:00:23 -070055import android.webkit.WebStorage;
56import android.webkit.WebView;
57import android.webkit.WebViewClient;
Grace Kloba22ac16e2009-10-07 18:00:23 -070058import android.widget.LinearLayout;
59import android.widget.TextView;
Ben Murdoch8029a772010-11-16 11:58:21 +000060import android.widget.Toast;
Grace Kloba22ac16e2009-10-07 18:00:23 -070061
Michael Kolbfe251992010-07-08 15:41:55 -070062import java.util.ArrayList;
63import java.util.HashMap;
64import java.util.Iterator;
65import java.util.LinkedList;
66import java.util.Map;
67import java.util.Vector;
68
Grace Kloba22ac16e2009-10-07 18:00:23 -070069/**
70 * Class for maintaining Tabs with a main WebView and a subwindow.
71 */
72class Tab {
Michael Kolb8233fac2010-10-26 16:08:53 -070073
Grace Kloba22ac16e2009-10-07 18:00:23 -070074 // Log Tag
75 private static final String LOGTAG = "Tab";
Ben Murdochc42addf2010-01-28 15:19:59 +000076 // Special case the logtag for messages for the Console to make it easier to
77 // filter them and match the logtag used for these messages in older versions
78 // of the browser.
79 private static final String CONSOLE_LOGTAG = "browser";
80
John Reck30c714c2010-12-16 17:30:34 -080081 public enum LockIcon {
82 LOCK_ICON_UNSECURE,
83 LOCK_ICON_SECURE,
84 LOCK_ICON_MIXED,
85 }
Michael Kolb8233fac2010-10-26 16:08:53 -070086
87 Activity mActivity;
88 private WebViewController mWebViewController;
89
Grace Kloba22ac16e2009-10-07 18:00:23 -070090 // The Geolocation permissions prompt
91 private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
92 // Main WebView wrapper
Michael Kolba713ec82010-11-29 17:27:06 -080093 private View mContainer;
Grace Kloba22ac16e2009-10-07 18:00:23 -070094 // Main WebView
95 private WebView mMainView;
96 // Subwindow container
97 private View mSubViewContainer;
98 // Subwindow WebView
99 private WebView mSubView;
100 // Saved bundle for when we are running low on memory. It contains the
101 // information needed to restore the WebView if the user goes back to the
102 // tab.
103 private Bundle mSavedState;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700104 // Parent Tab. This is the Tab that created this Tab, or null if the Tab was
105 // created by the UI
106 private Tab mParentTab;
107 // Tab that constructed by this Tab. This is used when this Tab is
108 // destroyed, it clears all mParentTab values in the children.
109 private Vector<Tab> mChildTabs;
110 // If true, the tab will be removed when back out of the first page.
111 private boolean mCloseOnExit;
112 // If true, the tab is in the foreground of the current activity.
113 private boolean mInForeground;
Michael Kolb8233fac2010-10-26 16:08:53 -0700114 // If true, the tab is in page loading state (after onPageStarted,
115 // before onPageFinsihed)
116 private boolean mInPageLoad;
John Reck30c714c2010-12-16 17:30:34 -0800117 // The last reported progress of the current page
118 private int mPageLoadProgress;
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000119 // The time the load started, used to find load page time
120 private long mLoadStartTime;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700121 // Application identifier used to find tabs that another application wants
122 // to reuse.
123 private String mAppId;
124 // Keep the original url around to avoid killing the old WebView if the url
125 // has not changed.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700126 // Error console for the tab
127 private ErrorConsoleView mErrorConsole;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -0500128 // The listener that gets invoked when a download is started from the
129 // mMainView
130 private final DownloadListener mDownloadListener;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500131 // Listener used to know when we move forward or back in the history list.
132 private final WebBackForwardListClient mWebBackForwardListClient;
John Recke969cc52010-12-21 17:24:43 -0800133 private DataController mDataController;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700134
135 // AsyncTask for downloading touch icons
136 DownloadTouchIcon mTouchIconLoader;
137
John Reck30c714c2010-12-16 17:30:34 -0800138 // All the state needed for a page
139 private static class PageState {
140 String mUrl;
141 String mTitle;
142 LockIcon mLockIcon;
143 Bitmap mFavicon;
John Recke969cc52010-12-21 17:24:43 -0800144 Boolean mIsBookmarkedSite = false;
John Reck30c714c2010-12-16 17:30:34 -0800145
146 PageState(Context c, boolean incognito) {
147 if (incognito) {
148 mUrl = "browser:incognito";
149 mTitle = c.getString(R.string.new_incognito_tab);
150 mFavicon = BitmapFactory.decodeResource(
151 c.getResources(), R.drawable.fav_incognito);
152 } else {
153 mUrl = "";
154 mTitle = c.getString(R.string.new_tab);
155 mFavicon = BitmapFactory.decodeResource(
156 c.getResources(), R.drawable.app_web_browser_sm);
157 }
158 mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
159 }
160
161 PageState(Context c, boolean incognito, String url, Bitmap favicon) {
162 mUrl = url;
163 mTitle = null;
164 if (URLUtil.isHttpsUrl(url)) {
165 mLockIcon = LockIcon.LOCK_ICON_SECURE;
166 } else {
167 mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
168 }
169 if (favicon != null) {
170 mFavicon = favicon;
171 } else {
172 if (incognito) {
173 mFavicon = BitmapFactory.decodeResource(
174 c.getResources(), R.drawable.fav_incognito);
175 } else {
176 mFavicon = BitmapFactory.decodeResource(
177 c.getResources(), R.drawable.app_web_browser_sm);
178 }
179 }
180 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700181 }
182
John Reck30c714c2010-12-16 17:30:34 -0800183 // The current/loading page's state
184 private PageState mCurrentState;
185
Grace Kloba22ac16e2009-10-07 18:00:23 -0700186 // Used for saving and restoring each Tab
John Reck30c714c2010-12-16 17:30:34 -0800187 // TODO: Figure out who uses what and where
188 // Some of these aren't use in this class, and some are only used in
189 // restoring state but not saving it - FIX THIS
Grace Kloba22ac16e2009-10-07 18:00:23 -0700190 static final String WEBVIEW = "webview";
191 static final String NUMTABS = "numTabs";
192 static final String CURRTAB = "currentTab";
193 static final String CURRURL = "currentUrl";
194 static final String CURRTITLE = "currentTitle";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700195 static final String CLOSEONEXIT = "closeonexit";
196 static final String PARENTTAB = "parentTab";
197 static final String APPID = "appid";
198 static final String ORIGINALURL = "originalUrl";
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700199 static final String INCOGNITO = "privateBrowsingEnabled";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700200
201 // -------------------------------------------------------------------------
202
Leon Scroggins58d56c62010-01-28 15:12:40 -0500203 /**
204 * Private information regarding the latest voice search. If the Tab is not
205 * in voice search mode, this will be null.
206 */
207 private VoiceSearchData mVoiceSearchData;
208 /**
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400209 * Remove voice search mode from this tab.
210 */
211 public void revertVoiceSearchMode() {
212 if (mVoiceSearchData != null) {
213 mVoiceSearchData = null;
214 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700215 mWebViewController.revertVoiceSearchMode(this);
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400216 }
217 }
218 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700219
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400220 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500221 * Return whether the tab is in voice search mode.
222 */
223 public boolean isInVoiceSearchMode() {
224 return mVoiceSearchData != null;
225 }
226 /**
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400227 * Return true if the Tab is in voice search mode and the voice search
228 * Intent came with a String identifying that Google provided the Intent.
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500229 */
230 public boolean voiceSearchSourceIsGoogle() {
231 return mVoiceSearchData != null && mVoiceSearchData.mSourceIsGoogle;
232 }
233 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500234 * Get the title to display for the current voice search page. If the Tab
235 * is not in voice search mode, return null.
236 */
237 public String getVoiceDisplayTitle() {
238 if (mVoiceSearchData == null) return null;
239 return mVoiceSearchData.mLastVoiceSearchTitle;
240 }
241 /**
242 * Get the latest array of voice search results, to be passed to the
243 * BrowserProvider. If the Tab is not in voice search mode, return null.
244 */
245 public ArrayList<String> getVoiceSearchResults() {
246 if (mVoiceSearchData == null) return null;
247 return mVoiceSearchData.mVoiceSearchResults;
248 }
249 /**
250 * Activate voice search mode.
251 * @param intent Intent which has the results to use, or an index into the
252 * results when reusing the old results.
253 */
254 /* package */ void activateVoiceSearchMode(Intent intent) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500255 int index = 0;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500256 ArrayList<String> results = intent.getStringArrayListExtra(
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500257 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_STRINGS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500258 if (results != null) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500259 ArrayList<String> urls = intent.getStringArrayListExtra(
260 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_URLS);
261 ArrayList<String> htmls = intent.getStringArrayListExtra(
262 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_HTML);
263 ArrayList<String> baseUrls = intent.getStringArrayListExtra(
264 RecognizerResultsIntent
265 .EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500266 // This tab is now entering voice search mode for the first time, or
267 // a new voice search was done.
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500268 int size = results.size();
269 if (urls == null || size != urls.size()) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500270 throw new AssertionError("improper extras passed in Intent");
271 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500272 if (htmls == null || htmls.size() != size || baseUrls == null ||
273 (baseUrls.size() != size && baseUrls.size() != 1)) {
274 // If either of these arrays are empty/incorrectly sized, ignore
275 // them.
276 htmls = null;
277 baseUrls = null;
278 }
279 mVoiceSearchData = new VoiceSearchData(results, urls, htmls,
280 baseUrls);
Leon Scroggins9df94972010-03-08 18:20:35 -0500281 mVoiceSearchData.mHeaders = intent.getParcelableArrayListExtra(
282 RecognizerResultsIntent
283 .EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS);
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500284 mVoiceSearchData.mSourceIsGoogle = intent.getBooleanExtra(
285 VoiceSearchData.SOURCE_IS_GOOGLE, false);
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400286 mVoiceSearchData.mVoiceSearchIntent = new Intent(intent);
Leon Scrogginse10dde52010-03-08 19:53:03 -0500287 }
288 String extraData = intent.getStringExtra(
289 SearchManager.EXTRA_DATA_KEY);
290 if (extraData != null) {
291 index = Integer.parseInt(extraData);
292 if (index >= mVoiceSearchData.mVoiceSearchResults.size()) {
293 throw new AssertionError("index must be less than "
294 + "size of mVoiceSearchResults");
295 }
296 if (mVoiceSearchData.mSourceIsGoogle) {
297 Intent logIntent = new Intent(
298 LoggingEvents.ACTION_LOG_EVENT);
299 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
300 LoggingEvents.VoiceSearch.N_BEST_CHOOSE);
301 logIntent.putExtra(
302 LoggingEvents.VoiceSearch.EXTRA_N_BEST_CHOOSE_INDEX,
303 index);
304 mActivity.sendBroadcast(logIntent);
305 }
306 if (mVoiceSearchData.mVoiceSearchIntent != null) {
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400307 // Copy the Intent, so that each history item will have its own
308 // Intent, with different (or none) extra data.
309 Intent latest = new Intent(mVoiceSearchData.mVoiceSearchIntent);
310 latest.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
311 mVoiceSearchData.mVoiceSearchIntent = latest;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500312 }
313 }
314 mVoiceSearchData.mLastVoiceSearchTitle
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500315 = mVoiceSearchData.mVoiceSearchResults.get(index);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500316 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700317 mWebViewController.activateVoiceSearchMode(mVoiceSearchData.mLastVoiceSearchTitle);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500318 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500319 if (mVoiceSearchData.mVoiceSearchHtmls != null) {
320 // When index was found it was already ensured that it was valid
321 String uriString = mVoiceSearchData.mVoiceSearchHtmls.get(index);
322 if (uriString != null) {
323 Uri dataUri = Uri.parse(uriString);
324 if (RecognizerResultsIntent.URI_SCHEME_INLINE.equals(
325 dataUri.getScheme())) {
326 // If there is only one base URL, use it. If there are
327 // more, there will be one for each index, so use the base
328 // URL corresponding to the index.
329 String baseUrl = mVoiceSearchData.mVoiceSearchBaseUrls.get(
330 mVoiceSearchData.mVoiceSearchBaseUrls.size() > 1 ?
331 index : 0);
332 mVoiceSearchData.mLastVoiceSearchUrl = baseUrl;
333 mMainView.loadDataWithBaseURL(baseUrl,
334 uriString.substring(RecognizerResultsIntent
335 .URI_SCHEME_INLINE.length() + 1), "text/html",
336 "utf-8", baseUrl);
337 return;
338 }
339 }
340 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500341 mVoiceSearchData.mLastVoiceSearchUrl
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500342 = mVoiceSearchData.mVoiceSearchUrls.get(index);
343 if (null == mVoiceSearchData.mLastVoiceSearchUrl) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700344 mVoiceSearchData.mLastVoiceSearchUrl = UrlUtils.smartUrlFilter(
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500345 mVoiceSearchData.mLastVoiceSearchTitle);
346 }
Leon Scroggins9df94972010-03-08 18:20:35 -0500347 Map<String, String> headers = null;
348 if (mVoiceSearchData.mHeaders != null) {
349 int bundleIndex = mVoiceSearchData.mHeaders.size() == 1 ? 0
350 : index;
351 Bundle bundle = mVoiceSearchData.mHeaders.get(bundleIndex);
352 if (bundle != null && !bundle.isEmpty()) {
353 Iterator<String> iter = bundle.keySet().iterator();
354 headers = new HashMap<String, String>();
355 while (iter.hasNext()) {
356 String key = iter.next();
357 headers.put(key, bundle.getString(key));
358 }
359 }
360 }
361 mMainView.loadUrl(mVoiceSearchData.mLastVoiceSearchUrl, headers);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500362 }
363 /* package */ static class VoiceSearchData {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500364 public VoiceSearchData(ArrayList<String> results,
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500365 ArrayList<String> urls, ArrayList<String> htmls,
366 ArrayList<String> baseUrls) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500367 mVoiceSearchResults = results;
368 mVoiceSearchUrls = urls;
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500369 mVoiceSearchHtmls = htmls;
370 mVoiceSearchBaseUrls = baseUrls;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500371 }
372 /*
373 * ArrayList of suggestions to be displayed when opening the
374 * SearchManager
375 */
376 public ArrayList<String> mVoiceSearchResults;
377 /*
378 * ArrayList of urls, associated with the suggestions in
379 * mVoiceSearchResults.
380 */
381 public ArrayList<String> mVoiceSearchUrls;
382 /*
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500383 * ArrayList holding content to load for each item in
384 * mVoiceSearchResults.
385 */
386 public ArrayList<String> mVoiceSearchHtmls;
387 /*
388 * ArrayList holding base urls for the items in mVoiceSearchResults.
389 * If non null, this will either have the same size as
390 * mVoiceSearchResults or have a size of 1, in which case all will use
391 * the same base url
392 */
393 public ArrayList<String> mVoiceSearchBaseUrls;
394 /*
Leon Scroggins58d56c62010-01-28 15:12:40 -0500395 * The last url provided by voice search. Used for comparison to see if
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500396 * we are going to a page by some method besides voice search.
Leon Scroggins58d56c62010-01-28 15:12:40 -0500397 */
398 public String mLastVoiceSearchUrl;
399 /**
400 * The last title used for voice search. Needed to update the title bar
401 * when switching tabs.
402 */
403 public String mLastVoiceSearchTitle;
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500404 /**
405 * Whether the Intent which turned on voice search mode contained the
406 * String signifying that Google was the source.
407 */
408 public boolean mSourceIsGoogle;
409 /**
Leon Scroggins9df94972010-03-08 18:20:35 -0500410 * List of headers to be passed into the WebView containing location
411 * information
412 */
413 public ArrayList<Bundle> mHeaders;
414 /**
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500415 * The Intent used to invoke voice search. Placed on the
416 * WebHistoryItem so that when coming back to a previous voice search
417 * page we can again activate voice search.
418 */
Leon Scrogginse10dde52010-03-08 19:53:03 -0500419 public Intent mVoiceSearchIntent;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500420 /**
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500421 * String used to identify Google as the source of voice search.
422 */
423 public static String SOURCE_IS_GOOGLE
424 = "android.speech.extras.SOURCE_IS_GOOGLE";
Leon Scroggins58d56c62010-01-28 15:12:40 -0500425 }
426
Grace Kloba22ac16e2009-10-07 18:00:23 -0700427 // Container class for the next error dialog that needs to be displayed
428 private class ErrorDialog {
429 public final int mTitle;
430 public final String mDescription;
431 public final int mError;
432 ErrorDialog(int title, String desc, int error) {
433 mTitle = title;
434 mDescription = desc;
435 mError = error;
436 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700437 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700438
439 private void processNextError() {
440 if (mQueuedErrors == null) {
441 return;
442 }
443 // The first one is currently displayed so just remove it.
444 mQueuedErrors.removeFirst();
445 if (mQueuedErrors.size() == 0) {
446 mQueuedErrors = null;
447 return;
448 }
449 showError(mQueuedErrors.getFirst());
450 }
451
452 private DialogInterface.OnDismissListener mDialogListener =
453 new DialogInterface.OnDismissListener() {
454 public void onDismiss(DialogInterface d) {
455 processNextError();
456 }
457 };
458 private LinkedList<ErrorDialog> mQueuedErrors;
459
460 private void queueError(int err, String desc) {
461 if (mQueuedErrors == null) {
462 mQueuedErrors = new LinkedList<ErrorDialog>();
463 }
464 for (ErrorDialog d : mQueuedErrors) {
465 if (d.mError == err) {
466 // Already saw a similar error, ignore the new one.
467 return;
468 }
469 }
470 ErrorDialog errDialog = new ErrorDialog(
471 err == WebViewClient.ERROR_FILE_NOT_FOUND ?
472 R.string.browserFrameFileErrorLabel :
473 R.string.browserFrameNetworkErrorLabel,
474 desc, err);
475 mQueuedErrors.addLast(errDialog);
476
477 // Show the dialog now if the queue was empty and it is in foreground
478 if (mQueuedErrors.size() == 1 && mInForeground) {
479 showError(errDialog);
480 }
481 }
482
483 private void showError(ErrorDialog errDialog) {
484 if (mInForeground) {
485 AlertDialog d = new AlertDialog.Builder(mActivity)
486 .setTitle(errDialog.mTitle)
487 .setMessage(errDialog.mDescription)
488 .setPositiveButton(R.string.ok, null)
489 .create();
490 d.setOnDismissListener(mDialogListener);
491 d.show();
492 }
493 }
494
495 // -------------------------------------------------------------------------
496 // WebViewClient implementation for the main WebView
497 // -------------------------------------------------------------------------
498
499 private final WebViewClient mWebViewClient = new WebViewClient() {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500500 private Message mDontResend;
501 private Message mResend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700502 @Override
503 public void onPageStarted(WebView view, String url, Bitmap favicon) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700504 mInPageLoad = true;
John Reck30c714c2010-12-16 17:30:34 -0800505 mPageLoadProgress = 0;
506 mCurrentState = new PageState(mActivity,
507 view.isPrivateBrowsingEnabled(), url, favicon);
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000508 mLoadStartTime = SystemClock.uptimeMillis();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500509 if (mVoiceSearchData != null
510 && !url.equals(mVoiceSearchData.mLastVoiceSearchUrl)) {
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500511 if (mVoiceSearchData.mSourceIsGoogle) {
512 Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
513 i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
514 mActivity.sendBroadcast(i);
515 }
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400516 revertVoiceSearchMode();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500517 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700518
Grace Kloba22ac16e2009-10-07 18:00:23 -0700519
520 // If we start a touch icon load and then load a new page, we don't
521 // want to cancel the current touch icon loader. But, we do want to
522 // create a new one when the touch icon url is known.
523 if (mTouchIconLoader != null) {
524 mTouchIconLoader.mTab = null;
525 mTouchIconLoader = null;
526 }
527
528 // reset the error console
529 if (mErrorConsole != null) {
530 mErrorConsole.clearErrorMessages();
Michael Kolb8233fac2010-10-26 16:08:53 -0700531 if (mWebViewController.shouldShowErrorConsole()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700532 mErrorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
533 }
534 }
535
Grace Kloba22ac16e2009-10-07 18:00:23 -0700536 // finally update the UI in the activity if it is in the foreground
John Reck324d4402011-01-11 16:56:42 -0800537 mWebViewController.onPageStarted(Tab.this, view, favicon);
Leon Scroggins4cd97792010-12-03 15:31:56 -0500538
John Recke969cc52010-12-21 17:24:43 -0800539 updateBookmarkedStatus();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700540 }
541
542 @Override
543 public void onPageFinished(WebView view, String url) {
John Reck5b691842010-11-29 11:21:13 -0800544 if (!isPrivateBrowsingEnabled()) {
545 LogTag.logPageFinishedLoading(
546 url, SystemClock.uptimeMillis() - mLoadStartTime);
547 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700548 mInPageLoad = false;
John Reck30c714c2010-12-16 17:30:34 -0800549 // Sync state (in case of stop/timeout)
550 mCurrentState.mUrl = view.getUrl();
John Reck6c702ee2011-01-07 09:41:53 -0800551 if (mCurrentState.mUrl == null) {
552 mCurrentState.mUrl = url != null ? url : "";
553 }
John Reck30c714c2010-12-16 17:30:34 -0800554 mCurrentState.mTitle = view.getTitle();
555 mCurrentState.mFavicon = view.getFavicon();
556 if (!URLUtil.isHttpsUrl(mCurrentState.mUrl)) {
557 // In case we stop when loading an HTTPS page from an HTTP page
558 // but before a provisional load occurred
559 mCurrentState.mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
560 }
John Reck324d4402011-01-11 16:56:42 -0800561 mWebViewController.onPageFinished(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700562 }
563
564 // return true if want to hijack the url to let another app to handle it
565 @Override
566 public boolean shouldOverrideUrlLoading(WebView view, String url) {
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400567 if (voiceSearchSourceIsGoogle()) {
568 // This method is called when the user clicks on a link.
569 // VoiceSearchMode is turned off when the user leaves the
570 // Google results page, so at this point the user must be on
571 // that page. If the user clicked a link on that page, assume
572 // that the voice search was effective, and broadcast an Intent
573 // so a receiver can take note of that fact.
574 Intent logIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
575 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
576 LoggingEvents.VoiceSearch.RESULT_CLICKED);
577 mActivity.sendBroadcast(logIntent);
578 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700579 if (mInForeground) {
Michael Kolb18eb3772010-12-10 14:29:51 -0800580 return mWebViewController.shouldOverrideUrlLoading(Tab.this,
581 view, url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700582 } else {
583 return false;
584 }
585 }
586
587 /**
588 * Updates the lock icon. This method is called when we discover another
589 * resource to be loaded for this page (for example, javascript). While
590 * we update the icon type, we do not update the lock icon itself until
591 * we are done loading, it is slightly more secure this way.
592 */
593 @Override
594 public void onLoadResource(WebView view, String url) {
595 if (url != null && url.length() > 0) {
596 // It is only if the page claims to be secure that we may have
597 // to update the lock:
John Reck30c714c2010-12-16 17:30:34 -0800598 if (mCurrentState.mLockIcon == LockIcon.LOCK_ICON_SECURE) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700599 // If NOT a 'safe' url, change the lock to mixed content!
600 if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url)
601 || URLUtil.isAboutUrl(url))) {
John Reck30c714c2010-12-16 17:30:34 -0800602 mCurrentState.mLockIcon = LockIcon.LOCK_ICON_MIXED;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700603 }
604 }
605 }
606 }
607
608 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -0700609 * Show a dialog informing the user of the network error reported by
610 * WebCore if it is in the foreground.
611 */
612 @Override
613 public void onReceivedError(WebView view, int errorCode,
614 String description, String failingUrl) {
615 if (errorCode != WebViewClient.ERROR_HOST_LOOKUP &&
616 errorCode != WebViewClient.ERROR_CONNECT &&
617 errorCode != WebViewClient.ERROR_BAD_URL &&
618 errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME &&
619 errorCode != WebViewClient.ERROR_FILE) {
620 queueError(errorCode, description);
621 }
Jeff Hamilton47654f42010-09-07 09:57:51 -0500622
623 // Don't log URLs when in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -0700624 if (!isPrivateBrowsingEnabled()) {
Jeff Hamilton47654f42010-09-07 09:57:51 -0500625 Log.e(LOGTAG, "onReceivedError " + errorCode + " " + failingUrl
626 + " " + description);
627 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700628 }
629
630 /**
631 * Check with the user if it is ok to resend POST data as the page they
632 * are trying to navigate to is the result of a POST.
633 */
634 @Override
635 public void onFormResubmission(WebView view, final Message dontResend,
636 final Message resend) {
637 if (!mInForeground) {
638 dontResend.sendToTarget();
639 return;
640 }
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500641 if (mDontResend != null) {
642 Log.w(LOGTAG, "onFormResubmission should not be called again "
643 + "while dialog is still up");
644 dontResend.sendToTarget();
645 return;
646 }
647 mDontResend = dontResend;
648 mResend = resend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700649 new AlertDialog.Builder(mActivity).setTitle(
650 R.string.browserFrameFormResubmitLabel).setMessage(
651 R.string.browserFrameFormResubmitMessage)
652 .setPositiveButton(R.string.ok,
653 new DialogInterface.OnClickListener() {
654 public void onClick(DialogInterface dialog,
655 int which) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500656 if (mResend != null) {
657 mResend.sendToTarget();
658 mResend = null;
659 mDontResend = null;
660 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700661 }
662 }).setNegativeButton(R.string.cancel,
663 new DialogInterface.OnClickListener() {
664 public void onClick(DialogInterface dialog,
665 int which) {
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 }).setOnCancelListener(new OnCancelListener() {
673 public void onCancel(DialogInterface dialog) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500674 if (mDontResend != null) {
675 mDontResend.sendToTarget();
676 mResend = null;
677 mDontResend = null;
678 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700679 }
680 }).show();
681 }
682
683 /**
684 * Insert the url into the visited history database.
685 * @param url The url to be inserted.
686 * @param isReload True if this url is being reloaded.
687 * FIXME: Not sure what to do when reloading the page.
688 */
689 @Override
690 public void doUpdateVisitedHistory(WebView view, String url,
691 boolean isReload) {
John Reck324d4402011-01-11 16:56:42 -0800692 mWebViewController.doUpdateVisitedHistory(Tab.this, isReload);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700693 }
694
695 /**
696 * Displays SSL error(s) dialog to the user.
697 */
698 @Override
699 public void onReceivedSslError(final WebView view,
700 final SslErrorHandler handler, final SslError error) {
701 if (!mInForeground) {
702 handler.cancel();
John Reck30c714c2010-12-16 17:30:34 -0800703 setLockIconType(LockIcon.LOCK_ICON_UNSECURE);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700704 return;
705 }
706 if (BrowserSettings.getInstance().showSecurityWarnings()) {
707 final LayoutInflater factory =
708 LayoutInflater.from(mActivity);
709 final View warningsView =
710 factory.inflate(R.layout.ssl_warnings, null);
711 final LinearLayout placeholder =
712 (LinearLayout)warningsView.findViewById(R.id.placeholder);
713
714 if (error.hasError(SslError.SSL_UNTRUSTED)) {
715 LinearLayout ll = (LinearLayout)factory
716 .inflate(R.layout.ssl_warning, null);
717 ((TextView)ll.findViewById(R.id.warning))
718 .setText(R.string.ssl_untrusted);
719 placeholder.addView(ll);
720 }
721
722 if (error.hasError(SslError.SSL_IDMISMATCH)) {
723 LinearLayout ll = (LinearLayout)factory
724 .inflate(R.layout.ssl_warning, null);
725 ((TextView)ll.findViewById(R.id.warning))
726 .setText(R.string.ssl_mismatch);
727 placeholder.addView(ll);
728 }
729
730 if (error.hasError(SslError.SSL_EXPIRED)) {
731 LinearLayout ll = (LinearLayout)factory
732 .inflate(R.layout.ssl_warning, null);
733 ((TextView)ll.findViewById(R.id.warning))
734 .setText(R.string.ssl_expired);
735 placeholder.addView(ll);
736 }
737
738 if (error.hasError(SslError.SSL_NOTYETVALID)) {
739 LinearLayout ll = (LinearLayout)factory
740 .inflate(R.layout.ssl_warning, null);
741 ((TextView)ll.findViewById(R.id.warning))
742 .setText(R.string.ssl_not_yet_valid);
743 placeholder.addView(ll);
744 }
745
746 new AlertDialog.Builder(mActivity).setTitle(
747 R.string.security_warning).setIcon(
748 android.R.drawable.ic_dialog_alert).setView(
749 warningsView).setPositiveButton(R.string.ssl_continue,
750 new DialogInterface.OnClickListener() {
751 public void onClick(DialogInterface dialog,
752 int whichButton) {
753 handler.proceed();
754 }
755 }).setNeutralButton(R.string.view_certificate,
756 new DialogInterface.OnClickListener() {
757 public void onClick(DialogInterface dialog,
758 int whichButton) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700759 mWebViewController.showSslCertificateOnError(view,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700760 handler, error);
761 }
Ben Murdocha49b8292010-11-16 11:56:04 +0000762 }).setNegativeButton(R.string.ssl_go_back,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700763 new DialogInterface.OnClickListener() {
764 public void onClick(DialogInterface dialog,
765 int whichButton) {
John Reck30c714c2010-12-16 17:30:34 -0800766 dialog.cancel();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700767 }
768 }).setOnCancelListener(
769 new DialogInterface.OnCancelListener() {
770 public void onCancel(DialogInterface dialog) {
771 handler.cancel();
John Reck30c714c2010-12-16 17:30:34 -0800772 setLockIconType(LockIcon.LOCK_ICON_UNSECURE);
773 mWebViewController.onUserCanceledSsl(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700774 }
775 }).show();
776 } else {
777 handler.proceed();
778 }
779 }
780
781 /**
782 * Handles an HTTP authentication request.
783 *
784 * @param handler The authentication handler
785 * @param host The host
786 * @param realm The realm
787 */
788 @Override
789 public void onReceivedHttpAuthRequest(WebView view,
790 final HttpAuthHandler handler, final String host,
791 final String realm) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700792 mWebViewController.onReceivedHttpAuthRequest(Tab.this, view, handler, host, realm);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700793 }
794
795 @Override
John Reck438bf462011-01-12 18:11:46 -0800796 public WebResourceResponse shouldInterceptRequest(WebView view,
797 String url) {
798 WebResourceResponse res = HomeProvider.shouldInterceptRequest(
799 mActivity, url);
800 return res;
801 }
802
803 @Override
Grace Kloba22ac16e2009-10-07 18:00:23 -0700804 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
805 if (!mInForeground) {
806 return false;
807 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700808 return mWebViewController.shouldOverrideKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700809 }
810
811 @Override
812 public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700813 if (!mInForeground) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700814 return;
815 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700816 mWebViewController.onUnhandledKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700817 }
818 };
819
820 // -------------------------------------------------------------------------
821 // WebChromeClient implementation for the main WebView
822 // -------------------------------------------------------------------------
823
824 private final WebChromeClient mWebChromeClient = new WebChromeClient() {
825 // Helper method to create a new tab or sub window.
826 private void createWindow(final boolean dialog, final Message msg) {
827 WebView.WebViewTransport transport =
828 (WebView.WebViewTransport) msg.obj;
829 if (dialog) {
830 createSubWindow();
Michael Kolb8233fac2010-10-26 16:08:53 -0700831 mWebViewController.attachSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700832 transport.setWebView(mSubView);
833 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -0700834 final Tab newTab = mWebViewController.openTabAndShow(
Michael Kolb18eb3772010-12-10 14:29:51 -0800835 Tab.this,
Michael Kolb8233fac2010-10-26 16:08:53 -0700836 IntentHandler.EMPTY_URL_DATA, false, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700837 if (newTab != Tab.this) {
838 Tab.this.addChildTab(newTab);
839 }
840 transport.setWebView(newTab.getWebView());
841 }
842 msg.sendToTarget();
843 }
844
845 @Override
846 public boolean onCreateWindow(WebView view, final boolean dialog,
847 final boolean userGesture, final Message resultMsg) {
848 // only allow new window or sub window for the foreground case
849 if (!mInForeground) {
850 return false;
851 }
852 // Short-circuit if we can't create any more tabs or sub windows.
853 if (dialog && mSubView != null) {
854 new AlertDialog.Builder(mActivity)
855 .setTitle(R.string.too_many_subwindows_dialog_title)
856 .setIcon(android.R.drawable.ic_dialog_alert)
857 .setMessage(R.string.too_many_subwindows_dialog_message)
858 .setPositiveButton(R.string.ok, null)
859 .show();
860 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700861 } else if (!mWebViewController.getTabControl().canCreateNewTab()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700862 new AlertDialog.Builder(mActivity)
863 .setTitle(R.string.too_many_windows_dialog_title)
864 .setIcon(android.R.drawable.ic_dialog_alert)
865 .setMessage(R.string.too_many_windows_dialog_message)
866 .setPositiveButton(R.string.ok, null)
867 .show();
868 return false;
869 }
870
871 // Short-circuit if this was a user gesture.
872 if (userGesture) {
873 createWindow(dialog, resultMsg);
874 return true;
875 }
876
877 // Allow the popup and create the appropriate window.
878 final AlertDialog.OnClickListener allowListener =
879 new AlertDialog.OnClickListener() {
880 public void onClick(DialogInterface d,
881 int which) {
882 createWindow(dialog, resultMsg);
883 }
884 };
885
886 // Block the popup by returning a null WebView.
887 final AlertDialog.OnClickListener blockListener =
888 new AlertDialog.OnClickListener() {
889 public void onClick(DialogInterface d, int which) {
890 resultMsg.sendToTarget();
891 }
892 };
893
894 // Build a confirmation dialog to display to the user.
895 final AlertDialog d =
896 new AlertDialog.Builder(mActivity)
897 .setTitle(R.string.attention)
898 .setIcon(android.R.drawable.ic_dialog_alert)
899 .setMessage(R.string.popup_window_attempt)
900 .setPositiveButton(R.string.allow, allowListener)
901 .setNegativeButton(R.string.block, blockListener)
902 .setCancelable(false)
903 .create();
904
905 // Show the confirmation dialog.
906 d.show();
907 return true;
908 }
909
910 @Override
Patrick Scotteb5061b2009-11-18 15:00:30 -0500911 public void onRequestFocus(WebView view) {
912 if (!mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700913 mWebViewController.switchToTab(mWebViewController.getTabControl().getTabIndex(
Patrick Scotteb5061b2009-11-18 15:00:30 -0500914 Tab.this));
915 }
916 }
917
918 @Override
Grace Kloba22ac16e2009-10-07 18:00:23 -0700919 public void onCloseWindow(WebView window) {
920 if (mParentTab != null) {
921 // JavaScript can only close popup window.
922 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700923 mWebViewController.switchToTab(mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -0700924 .getTabIndex(mParentTab));
925 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700926 mWebViewController.closeTab(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700927 }
928 }
929
930 @Override
931 public void onProgressChanged(WebView view, int newProgress) {
John Reck30c714c2010-12-16 17:30:34 -0800932 mPageLoadProgress = newProgress;
933 mWebViewController.onProgressChanged(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700934 }
935
936 @Override
Leon Scroggins21d9b902010-03-11 09:33:11 -0500937 public void onReceivedTitle(WebView view, final String title) {
John Reck30c714c2010-12-16 17:30:34 -0800938 mCurrentState.mTitle = title;
Michael Kolb8233fac2010-10-26 16:08:53 -0700939 mWebViewController.onReceivedTitle(Tab.this, title);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700940 }
941
942 @Override
943 public void onReceivedIcon(WebView view, Bitmap icon) {
John Reck30c714c2010-12-16 17:30:34 -0800944 mCurrentState.mFavicon = icon;
Michael Kolb8233fac2010-10-26 16:08:53 -0700945 mWebViewController.onFavicon(Tab.this, view, icon);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700946 }
947
948 @Override
949 public void onReceivedTouchIconUrl(WebView view, String url,
950 boolean precomposed) {
951 final ContentResolver cr = mActivity.getContentResolver();
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400952 // Let precomposed icons take precedence over non-composed
953 // icons.
954 if (precomposed && mTouchIconLoader != null) {
955 mTouchIconLoader.cancel(false);
956 mTouchIconLoader = null;
957 }
958 // Have only one async task at a time.
959 if (mTouchIconLoader == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700960 mTouchIconLoader = new DownloadTouchIcon(Tab.this,
961 mActivity, cr, view);
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400962 mTouchIconLoader.execute(url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700963 }
964 }
965
966 @Override
967 public void onShowCustomView(View view,
968 WebChromeClient.CustomViewCallback callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700969 if (mInForeground) mWebViewController.showCustomView(Tab.this, view,
970 callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700971 }
972
973 @Override
974 public void onHideCustomView() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700975 if (mInForeground) mWebViewController.hideCustomView();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700976 }
977
978 /**
979 * The origin has exceeded its database quota.
980 * @param url the URL that exceeded the quota
981 * @param databaseIdentifier the identifier of the database on which the
982 * transaction that caused the quota overflow was run
983 * @param currentQuota the current quota for the origin.
984 * @param estimatedSize the estimated size of the database.
985 * @param totalUsedQuota is the sum of all origins' quota.
986 * @param quotaUpdater The callback to run when a decision to allow or
987 * deny quota has been made. Don't forget to call this!
988 */
989 @Override
990 public void onExceededDatabaseQuota(String url,
991 String databaseIdentifier, long currentQuota, long estimatedSize,
992 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
993 BrowserSettings.getInstance().getWebStorageSizeManager()
994 .onExceededDatabaseQuota(url, databaseIdentifier,
995 currentQuota, estimatedSize, totalUsedQuota,
996 quotaUpdater);
997 }
998
999 /**
1000 * The Application Cache has exceeded its max size.
1001 * @param spaceNeeded is the amount of disk space that would be needed
1002 * in order for the last appcache operation to succeed.
1003 * @param totalUsedQuota is the sum of all origins' quota.
1004 * @param quotaUpdater A callback to inform the WebCore thread that a
1005 * new app cache size is available. This callback must always
1006 * be executed at some point to ensure that the sleeping
1007 * WebCore thread is woken up.
1008 */
1009 @Override
1010 public void onReachedMaxAppCacheSize(long spaceNeeded,
1011 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
1012 BrowserSettings.getInstance().getWebStorageSizeManager()
1013 .onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
1014 quotaUpdater);
1015 }
1016
1017 /**
1018 * Instructs the browser to show a prompt to ask the user to set the
1019 * Geolocation permission state for the specified origin.
1020 * @param origin The origin for which Geolocation permissions are
1021 * requested.
1022 * @param callback The callback to call once the user has set the
1023 * Geolocation permission state.
1024 */
1025 @Override
1026 public void onGeolocationPermissionsShowPrompt(String origin,
1027 GeolocationPermissions.Callback callback) {
1028 if (mInForeground) {
Grace Kloba50c241e2010-04-20 11:07:50 -07001029 getGeolocationPermissionsPrompt().show(origin, callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001030 }
1031 }
1032
1033 /**
1034 * Instructs the browser to hide the Geolocation permissions prompt.
1035 */
1036 @Override
1037 public void onGeolocationPermissionsHidePrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001038 if (mInForeground && mGeolocationPermissionsPrompt != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001039 mGeolocationPermissionsPrompt.hide();
1040 }
1041 }
1042
Ben Murdoch65acc352009-11-19 18:16:04 +00001043 /* Adds a JavaScript error message to the system log and if the JS
1044 * console is enabled in the about:debug options, to that console
1045 * also.
Ben Murdochc42addf2010-01-28 15:19:59 +00001046 * @param consoleMessage the message object.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001047 */
1048 @Override
Ben Murdochc42addf2010-01-28 15:19:59 +00001049 public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001050 if (mInForeground) {
1051 // call getErrorConsole(true) so it will create one if needed
1052 ErrorConsoleView errorConsole = getErrorConsole(true);
Ben Murdochc42addf2010-01-28 15:19:59 +00001053 errorConsole.addErrorMessage(consoleMessage);
Michael Kolb8233fac2010-10-26 16:08:53 -07001054 if (mWebViewController.shouldShowErrorConsole()
1055 && errorConsole.getShowState() !=
1056 ErrorConsoleView.SHOW_MAXIMIZED) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001057 errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED);
1058 }
1059 }
Ben Murdochc42addf2010-01-28 15:19:59 +00001060
Jeff Hamilton47654f42010-09-07 09:57:51 -05001061 // Don't log console messages in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001062 if (isPrivateBrowsingEnabled()) return true;
Jeff Hamilton47654f42010-09-07 09:57:51 -05001063
Ben Murdochc42addf2010-01-28 15:19:59 +00001064 String message = "Console: " + consoleMessage.message() + " "
1065 + consoleMessage.sourceId() + ":"
1066 + consoleMessage.lineNumber();
1067
1068 switch (consoleMessage.messageLevel()) {
1069 case TIP:
1070 Log.v(CONSOLE_LOGTAG, message);
1071 break;
1072 case LOG:
1073 Log.i(CONSOLE_LOGTAG, message);
1074 break;
1075 case WARNING:
1076 Log.w(CONSOLE_LOGTAG, message);
1077 break;
1078 case ERROR:
1079 Log.e(CONSOLE_LOGTAG, message);
1080 break;
1081 case DEBUG:
1082 Log.d(CONSOLE_LOGTAG, message);
1083 break;
1084 }
1085
1086 return true;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001087 }
1088
1089 /**
1090 * Ask the browser for an icon to represent a <video> element.
1091 * This icon will be used if the Web page did not specify a poster attribute.
1092 * @return Bitmap The icon or null if no such icon is available.
1093 */
1094 @Override
1095 public Bitmap getDefaultVideoPoster() {
1096 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001097 return mWebViewController.getDefaultVideoPoster();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001098 }
1099 return null;
1100 }
1101
1102 /**
1103 * Ask the host application for a custom progress view to show while
1104 * a <video> is loading.
1105 * @return View The progress view.
1106 */
1107 @Override
1108 public View getVideoLoadingProgressView() {
1109 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001110 return mWebViewController.getVideoLoadingProgressView();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001111 }
1112 return null;
1113 }
1114
1115 @Override
Ben Murdoch62b1b7e2010-05-19 20:38:56 +01001116 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001117 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001118 mWebViewController.openFileChooser(uploadMsg, acceptType);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001119 } else {
1120 uploadMsg.onReceiveValue(null);
1121 }
1122 }
1123
1124 /**
1125 * Deliver a list of already-visited URLs
1126 */
1127 @Override
1128 public void getVisitedHistory(final ValueCallback<String[]> callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001129 mWebViewController.getVisitedHistory(callback);
1130 }
Ben Murdoch8029a772010-11-16 11:58:21 +00001131
1132 @Override
1133 public void setupAutoFill(Message message) {
1134 // Prompt the user to set up their profile.
1135 final Message msg = message;
1136 AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
1137 builder.setMessage(R.string.autofill_setup_dialog_message)
1138 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
1139 @Override
1140 public void onClick(DialogInterface dialog, int id) {
1141 // Take user to the AutoFill profile editor. When they return,
1142 // we will send the message that we pass here which will trigger
1143 // the form to get filled out with their new profile.
1144 mWebViewController.setupAutoFill(msg);
1145 }
1146 })
1147 .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
1148 @Override
1149 public void onClick(DialogInterface dialog, int id) {
1150 // Disable autofill and show a toast with how to turn it on again.
1151 BrowserSettings s = BrowserSettings.getInstance();
1152 s.addObserver(mMainView.getSettings());
1153 s.disableAutoFill(mActivity);
1154 s.update();
1155 Toast.makeText(mActivity, R.string.autofill_setup_dialog_negative_toast,
1156 Toast.LENGTH_LONG).show();
1157 }
1158 }).show();
1159 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001160 };
1161
1162 // -------------------------------------------------------------------------
1163 // WebViewClient implementation for the sub window
1164 // -------------------------------------------------------------------------
1165
1166 // Subclass of WebViewClient used in subwindows to notify the main
1167 // WebViewClient of certain WebView activities.
1168 private static class SubWindowClient extends WebViewClient {
1169 // The main WebViewClient.
1170 private final WebViewClient mClient;
Michael Kolb8233fac2010-10-26 16:08:53 -07001171 private final WebViewController mController;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001172
Michael Kolb8233fac2010-10-26 16:08:53 -07001173 SubWindowClient(WebViewClient client, WebViewController controller) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001174 mClient = client;
Michael Kolb8233fac2010-10-26 16:08:53 -07001175 mController = controller;
Leon Scroggins III211ba542010-04-19 13:21:13 -04001176 }
1177 @Override
1178 public void onPageStarted(WebView view, String url, Bitmap favicon) {
1179 // Unlike the others, do not call mClient's version, which would
1180 // change the progress bar. However, we do want to remove the
Cary Clark01cfcdd2010-06-04 16:36:45 -04001181 // find or select dialog.
Michael Kolb8233fac2010-10-26 16:08:53 -07001182 mController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001183 }
1184 @Override
1185 public void doUpdateVisitedHistory(WebView view, String url,
1186 boolean isReload) {
1187 mClient.doUpdateVisitedHistory(view, url, isReload);
1188 }
1189 @Override
1190 public boolean shouldOverrideUrlLoading(WebView view, String url) {
1191 return mClient.shouldOverrideUrlLoading(view, url);
1192 }
1193 @Override
1194 public void onReceivedSslError(WebView view, SslErrorHandler handler,
1195 SslError error) {
1196 mClient.onReceivedSslError(view, handler, error);
1197 }
1198 @Override
1199 public void onReceivedHttpAuthRequest(WebView view,
1200 HttpAuthHandler handler, String host, String realm) {
1201 mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
1202 }
1203 @Override
1204 public void onFormResubmission(WebView view, Message dontResend,
1205 Message resend) {
1206 mClient.onFormResubmission(view, dontResend, resend);
1207 }
1208 @Override
1209 public void onReceivedError(WebView view, int errorCode,
1210 String description, String failingUrl) {
1211 mClient.onReceivedError(view, errorCode, description, failingUrl);
1212 }
1213 @Override
1214 public boolean shouldOverrideKeyEvent(WebView view,
1215 android.view.KeyEvent event) {
1216 return mClient.shouldOverrideKeyEvent(view, event);
1217 }
1218 @Override
1219 public void onUnhandledKeyEvent(WebView view,
1220 android.view.KeyEvent event) {
1221 mClient.onUnhandledKeyEvent(view, event);
1222 }
1223 }
1224
1225 // -------------------------------------------------------------------------
1226 // WebChromeClient implementation for the sub window
1227 // -------------------------------------------------------------------------
1228
1229 private class SubWindowChromeClient extends WebChromeClient {
1230 // The main WebChromeClient.
1231 private final WebChromeClient mClient;
1232
1233 SubWindowChromeClient(WebChromeClient client) {
1234 mClient = client;
1235 }
1236 @Override
1237 public void onProgressChanged(WebView view, int newProgress) {
1238 mClient.onProgressChanged(view, newProgress);
1239 }
1240 @Override
1241 public boolean onCreateWindow(WebView view, boolean dialog,
1242 boolean userGesture, android.os.Message resultMsg) {
1243 return mClient.onCreateWindow(view, dialog, userGesture, resultMsg);
1244 }
1245 @Override
1246 public void onCloseWindow(WebView window) {
1247 if (window != mSubView) {
1248 Log.e(LOGTAG, "Can't close the window");
1249 }
Michael Kolb8233fac2010-10-26 16:08:53 -07001250 mWebViewController.dismissSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001251 }
1252 }
1253
1254 // -------------------------------------------------------------------------
1255
Michael Kolb8233fac2010-10-26 16:08:53 -07001256 // TODO temporarily use activity here
1257 // remove later
1258
Grace Kloba22ac16e2009-10-07 18:00:23 -07001259 // Construct a new tab
Michael Kolb8233fac2010-10-26 16:08:53 -07001260 Tab(WebViewController wvcontroller, WebView w, boolean closeOnExit, String appId,
Grace Kloba22ac16e2009-10-07 18:00:23 -07001261 String url) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001262 mWebViewController = wvcontroller;
1263 mActivity = mWebViewController.getActivity();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001264 mCloseOnExit = closeOnExit;
1265 mAppId = appId;
John Recke969cc52010-12-21 17:24:43 -08001266 mDataController = DataController.getInstance(mActivity);
John Reck30c714c2010-12-16 17:30:34 -08001267 mCurrentState = new PageState(mActivity, w.isPrivateBrowsingEnabled());
Michael Kolb8233fac2010-10-26 16:08:53 -07001268 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001269 mInForeground = false;
1270
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001271 mDownloadListener = new DownloadListener() {
1272 public void onDownloadStart(String url, String userAgent,
1273 String contentDisposition, String mimetype,
1274 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001275 mWebViewController.onDownloadStart(Tab.this, url, userAgent, contentDisposition,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001276 mimetype, contentLength);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001277 }
1278 };
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001279 mWebBackForwardListClient = new WebBackForwardListClient() {
1280 @Override
1281 public void onNewHistoryItem(WebHistoryItem item) {
1282 if (isInVoiceSearchMode()) {
1283 item.setCustomData(mVoiceSearchData.mVoiceSearchIntent);
1284 }
1285 }
1286 @Override
1287 public void onIndexChanged(WebHistoryItem item, int index) {
1288 Object data = item.getCustomData();
1289 if (data != null && data instanceof Intent) {
1290 activateVoiceSearchMode((Intent) data);
1291 }
1292 }
1293 };
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001294
Grace Kloba22ac16e2009-10-07 18:00:23 -07001295 setWebView(w);
1296 }
1297
1298 /**
1299 * Sets the WebView for this tab, correctly removing the old WebView from
1300 * the container view.
1301 */
1302 void setWebView(WebView w) {
1303 if (mMainView == w) {
1304 return;
1305 }
Michael Kolba713ec82010-11-29 17:27:06 -08001306
Grace Kloba22ac16e2009-10-07 18:00:23 -07001307 // If the WebView is changing, the page will be reloaded, so any ongoing
1308 // Geolocation permission requests are void.
Grace Kloba50c241e2010-04-20 11:07:50 -07001309 if (mGeolocationPermissionsPrompt != null) {
1310 mGeolocationPermissionsPrompt.hide();
1311 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001312
Michael Kolba713ec82010-11-29 17:27:06 -08001313 mWebViewController.onSetWebView(this, w);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001314
1315 // set the new one
1316 mMainView = w;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001317 // attach the WebViewClient, WebChromeClient and DownloadListener
Grace Kloba22ac16e2009-10-07 18:00:23 -07001318 if (mMainView != null) {
1319 mMainView.setWebViewClient(mWebViewClient);
1320 mMainView.setWebChromeClient(mWebChromeClient);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001321 // Attach DownloadManager so that downloads can start in an active
1322 // or a non-active window. This can happen when going to a site that
1323 // does a redirect after a period of time. The user could have
1324 // switched to another tab while waiting for the download to start.
1325 mMainView.setDownloadListener(mDownloadListener);
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001326 mMainView.setWebBackForwardListClient(mWebBackForwardListClient);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001327 }
1328 }
1329
1330 /**
1331 * Destroy the tab's main WebView and subWindow if any
1332 */
1333 void destroy() {
1334 if (mMainView != null) {
1335 dismissSubWindow();
1336 BrowserSettings.getInstance().deleteObserver(mMainView.getSettings());
1337 // save the WebView to call destroy() after detach it from the tab
1338 WebView webView = mMainView;
1339 setWebView(null);
1340 webView.destroy();
1341 }
1342 }
1343
1344 /**
1345 * Remove the tab from the parent
1346 */
1347 void removeFromTree() {
1348 // detach the children
1349 if (mChildTabs != null) {
1350 for(Tab t : mChildTabs) {
1351 t.setParentTab(null);
1352 }
1353 }
1354 // remove itself from the parent list
1355 if (mParentTab != null) {
1356 mParentTab.mChildTabs.remove(this);
1357 }
1358 }
1359
1360 /**
1361 * Create a new subwindow unless a subwindow already exists.
1362 * @return True if a new subwindow was created. False if one already exists.
1363 */
1364 boolean createSubWindow() {
1365 if (mSubView == null) {
Michael Kolb1514bb72010-11-22 09:11:48 -08001366 mWebViewController.createSubWindow(this);
Leon Scroggins III211ba542010-04-19 13:21:13 -04001367 mSubView.setWebViewClient(new SubWindowClient(mWebViewClient,
Michael Kolb8233fac2010-10-26 16:08:53 -07001368 mWebViewController));
Grace Kloba22ac16e2009-10-07 18:00:23 -07001369 mSubView.setWebChromeClient(new SubWindowChromeClient(
1370 mWebChromeClient));
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001371 // Set a different DownloadListener for the mSubView, since it will
1372 // just need to dismiss the mSubView, rather than close the Tab
1373 mSubView.setDownloadListener(new DownloadListener() {
1374 public void onDownloadStart(String url, String userAgent,
1375 String contentDisposition, String mimetype,
1376 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001377 mWebViewController.onDownloadStart(Tab.this, url, userAgent,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001378 contentDisposition, mimetype, contentLength);
1379 if (mSubView.copyBackForwardList().getSize() == 0) {
1380 // This subwindow was opened for the sole purpose of
1381 // downloading a file. Remove it.
Michael Kolb8233fac2010-10-26 16:08:53 -07001382 mWebViewController.dismissSubWindow(Tab.this);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001383 }
1384 }
1385 });
Grace Kloba22ac16e2009-10-07 18:00:23 -07001386 mSubView.setOnCreateContextMenuListener(mActivity);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001387 return true;
1388 }
1389 return false;
1390 }
1391
1392 /**
1393 * Dismiss the subWindow for the tab.
1394 */
1395 void dismissSubWindow() {
1396 if (mSubView != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001397 mWebViewController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001398 BrowserSettings.getInstance().deleteObserver(
1399 mSubView.getSettings());
1400 mSubView.destroy();
1401 mSubView = null;
1402 mSubViewContainer = null;
1403 }
1404 }
1405
Grace Kloba22ac16e2009-10-07 18:00:23 -07001406
1407 /**
1408 * Set the parent tab of this tab.
1409 */
1410 void setParentTab(Tab parent) {
1411 mParentTab = parent;
1412 // This tab may have been freed due to low memory. If that is the case,
1413 // the parent tab index is already saved. If we are changing that index
1414 // (most likely due to removing the parent tab) we must update the
1415 // parent tab index in the saved Bundle.
1416 if (mSavedState != null) {
1417 if (parent == null) {
1418 mSavedState.remove(PARENTTAB);
1419 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -07001420 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -07001421 .getTabIndex(parent));
1422 }
1423 }
1424 }
1425
1426 /**
1427 * When a Tab is created through the content of another Tab, then we
1428 * associate the Tabs.
1429 * @param child the Tab that was created from this Tab
1430 */
1431 void addChildTab(Tab child) {
1432 if (mChildTabs == null) {
1433 mChildTabs = new Vector<Tab>();
1434 }
1435 mChildTabs.add(child);
1436 child.setParentTab(this);
1437 }
1438
1439 Vector<Tab> getChildTabs() {
1440 return mChildTabs;
1441 }
1442
1443 void resume() {
1444 if (mMainView != null) {
1445 mMainView.onResume();
1446 if (mSubView != null) {
1447 mSubView.onResume();
1448 }
1449 }
1450 }
1451
1452 void pause() {
1453 if (mMainView != null) {
1454 mMainView.onPause();
1455 if (mSubView != null) {
1456 mSubView.onPause();
1457 }
1458 }
1459 }
1460
1461 void putInForeground() {
1462 mInForeground = true;
1463 resume();
1464 mMainView.setOnCreateContextMenuListener(mActivity);
1465 if (mSubView != null) {
1466 mSubView.setOnCreateContextMenuListener(mActivity);
1467 }
1468 // Show the pending error dialog if the queue is not empty
1469 if (mQueuedErrors != null && mQueuedErrors.size() > 0) {
1470 showError(mQueuedErrors.getFirst());
1471 }
Leon Scroggins1961ed22010-12-07 15:22:21 -05001472 mWebViewController.bookmarkedStatusHasChanged(this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001473 }
1474
1475 void putInBackground() {
1476 mInForeground = false;
1477 pause();
1478 mMainView.setOnCreateContextMenuListener(null);
1479 if (mSubView != null) {
1480 mSubView.setOnCreateContextMenuListener(null);
1481 }
1482 }
1483
Michael Kolb8233fac2010-10-26 16:08:53 -07001484 boolean inForeground() {
1485 return mInForeground;
1486 }
1487
Grace Kloba22ac16e2009-10-07 18:00:23 -07001488 /**
1489 * Return the top window of this tab; either the subwindow if it is not
1490 * null or the main window.
1491 * @return The top window of this tab.
1492 */
1493 WebView getTopWindow() {
1494 if (mSubView != null) {
1495 return mSubView;
1496 }
1497 return mMainView;
1498 }
1499
1500 /**
1501 * Return the main window of this tab. Note: if a tab is freed in the
1502 * background, this can return null. It is only guaranteed to be
1503 * non-null for the current tab.
1504 * @return The main WebView of this tab.
1505 */
1506 WebView getWebView() {
1507 return mMainView;
1508 }
1509
Michael Kolba713ec82010-11-29 17:27:06 -08001510 void setViewContainer(View container) {
1511 mContainer = container;
1512 }
1513
Michael Kolb8233fac2010-10-26 16:08:53 -07001514 View getViewContainer() {
1515 return mContainer;
1516 }
1517
Grace Kloba22ac16e2009-10-07 18:00:23 -07001518 /**
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001519 * Return whether private browsing is enabled for the main window of
1520 * this tab.
1521 * @return True if private browsing is enabled.
1522 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001523 boolean isPrivateBrowsingEnabled() {
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001524 WebView webView = getWebView();
1525 if (webView == null) {
1526 return false;
1527 }
1528 return webView.isPrivateBrowsingEnabled();
1529 }
1530
1531 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -07001532 * Return the subwindow of this tab or null if there is no subwindow.
1533 * @return The subwindow of this tab or null.
1534 */
1535 WebView getSubWebView() {
1536 return mSubView;
1537 }
1538
Michael Kolb1514bb72010-11-22 09:11:48 -08001539 void setSubWebView(WebView subView) {
1540 mSubView = subView;
1541 }
1542
Michael Kolb8233fac2010-10-26 16:08:53 -07001543 View getSubViewContainer() {
1544 return mSubViewContainer;
1545 }
1546
Michael Kolb1514bb72010-11-22 09:11:48 -08001547 void setSubViewContainer(View subViewContainer) {
1548 mSubViewContainer = subViewContainer;
1549 }
1550
Grace Kloba22ac16e2009-10-07 18:00:23 -07001551 /**
1552 * @return The geolocation permissions prompt for this tab.
1553 */
1554 GeolocationPermissionsPrompt getGeolocationPermissionsPrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001555 if (mGeolocationPermissionsPrompt == null) {
1556 ViewStub stub = (ViewStub) mContainer
1557 .findViewById(R.id.geolocation_permissions_prompt);
1558 mGeolocationPermissionsPrompt = (GeolocationPermissionsPrompt) stub
1559 .inflate();
1560 mGeolocationPermissionsPrompt.init();
1561 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001562 return mGeolocationPermissionsPrompt;
1563 }
1564
1565 /**
1566 * @return The application id string
1567 */
1568 String getAppId() {
1569 return mAppId;
1570 }
1571
1572 /**
1573 * Set the application id string
1574 * @param id
1575 */
1576 void setAppId(String id) {
1577 mAppId = id;
1578 }
1579
Grace Kloba22ac16e2009-10-07 18:00:23 -07001580 String getUrl() {
John Reck324d4402011-01-11 16:56:42 -08001581 return UrlUtils.filteredUrl(mCurrentState.mUrl);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001582 }
1583
1584 /**
John Reck30c714c2010-12-16 17:30:34 -08001585 * Get the title of this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001586 */
1587 String getTitle() {
John Reck30c714c2010-12-16 17:30:34 -08001588 if (mCurrentState.mTitle == null && mInPageLoad) {
1589 return mActivity.getString(R.string.title_bar_loading);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001590 }
John Reck30c714c2010-12-16 17:30:34 -08001591 return mCurrentState.mTitle;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001592 }
1593
1594 /**
John Reck30c714c2010-12-16 17:30:34 -08001595 * Get the favicon of this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001596 */
1597 Bitmap getFavicon() {
John Reck30c714c2010-12-16 17:30:34 -08001598 return mCurrentState.mFavicon;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001599 }
1600
John Recke969cc52010-12-21 17:24:43 -08001601 public boolean isBookmarkedSite() {
1602 return mCurrentState.mIsBookmarkedSite;
1603 }
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001604
Grace Kloba22ac16e2009-10-07 18:00:23 -07001605 /**
1606 * Return the tab's error console. Creates the console if createIfNEcessary
1607 * is true and we haven't already created the console.
1608 * @param createIfNecessary Flag to indicate if the console should be
1609 * created if it has not been already.
1610 * @return The tab's error console, or null if one has not been created and
1611 * createIfNecessary is false.
1612 */
1613 ErrorConsoleView getErrorConsole(boolean createIfNecessary) {
1614 if (createIfNecessary && mErrorConsole == null) {
1615 mErrorConsole = new ErrorConsoleView(mActivity);
1616 mErrorConsole.setWebView(mMainView);
1617 }
1618 return mErrorConsole;
1619 }
1620
1621 /**
1622 * If this Tab was created through another Tab, then this method returns
1623 * that Tab.
1624 * @return the Tab parent or null
1625 */
1626 public Tab getParentTab() {
1627 return mParentTab;
1628 }
1629
1630 /**
1631 * Return whether this tab should be closed when it is backing out of the
1632 * first page.
1633 * @return TRUE if this tab should be closed when exit.
1634 */
1635 boolean closeOnExit() {
1636 return mCloseOnExit;
1637 }
1638
John Reck30c714c2010-12-16 17:30:34 -08001639 private void setLockIconType(LockIcon icon) {
1640 mCurrentState.mLockIcon = icon;
1641 mWebViewController.onUpdatedLockIcon(this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001642 }
1643
1644 /**
1645 * @return The tab's lock icon type.
1646 */
John Reck30c714c2010-12-16 17:30:34 -08001647 LockIcon getLockIconType() {
1648 return mCurrentState.mLockIcon;
1649 }
1650
1651 int getLoadProgress() {
1652 if (mInPageLoad) {
1653 return mPageLoadProgress;
1654 }
1655 return 100;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001656 }
1657
1658 /**
1659 * @return TRUE if onPageStarted is called while onPageFinished is not
1660 * called yet.
1661 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001662 boolean inPageLoad() {
1663 return mInPageLoad;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001664 }
1665
1666 // force mInLoad to be false. This should only be called before closing the
1667 // tab to ensure BrowserActivity's pauseWebViewTimers() is called correctly.
Michael Kolb8233fac2010-10-26 16:08:53 -07001668 void clearInPageLoad() {
1669 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001670 }
1671
Grace Kloba22ac16e2009-10-07 18:00:23 -07001672 /**
John Reck30c714c2010-12-16 17:30:34 -08001673 * Get the cached saved state bundle.
1674 * @return cached state bundle
Grace Kloba22ac16e2009-10-07 18:00:23 -07001675 */
1676 Bundle getSavedState() {
1677 return mSavedState;
1678 }
1679
1680 /**
1681 * Set the saved state.
1682 */
1683 void setSavedState(Bundle state) {
1684 mSavedState = state;
1685 }
1686
1687 /**
1688 * @return TRUE if succeed in saving the state.
1689 */
1690 boolean saveState() {
1691 // If the WebView is null it means we ran low on memory and we already
1692 // stored the saved state in mSavedState.
1693 if (mMainView == null) {
1694 return mSavedState != null;
1695 }
1696
1697 mSavedState = new Bundle();
1698 final WebBackForwardList list = mMainView.saveState(mSavedState);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001699
1700 // Store some extra info for displaying the tab in the picker.
1701 final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001702
John Reck30c714c2010-12-16 17:30:34 -08001703 mSavedState.putString(CURRURL, mCurrentState.mUrl);
1704 mSavedState.putString(CURRTITLE, mCurrentState.mTitle);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001705 mSavedState.putBoolean(CLOSEONEXIT, mCloseOnExit);
1706 if (mAppId != null) {
1707 mSavedState.putString(APPID, mAppId);
1708 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001709 // Remember the parent tab so the relationship can be restored.
1710 if (mParentTab != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001711 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl().getTabIndex(
Grace Kloba22ac16e2009-10-07 18:00:23 -07001712 mParentTab));
1713 }
1714 return true;
1715 }
1716
1717 /*
1718 * Restore the state of the tab.
1719 */
1720 boolean restoreState(Bundle b) {
1721 if (b == null) {
1722 return false;
1723 }
1724 // Restore the internal state even if the WebView fails to restore.
1725 // This will maintain the app id, original url and close-on-exit values.
1726 mSavedState = null;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001727 mCloseOnExit = b.getBoolean(CLOSEONEXIT);
1728 mAppId = b.getString(APPID);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001729
1730 final WebBackForwardList list = mMainView.restoreState(b);
1731 if (list == null) {
1732 return false;
1733 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001734 return true;
1735 }
Leon Scroggins III211ba542010-04-19 13:21:13 -04001736
Leon Scroggins1961ed22010-12-07 15:22:21 -05001737 public void updateBookmarkedStatus() {
John Recke969cc52010-12-21 17:24:43 -08001738 mDataController.queryBookmarkStatus(getUrl(), mIsBookmarkCallback);
Leon Scroggins1961ed22010-12-07 15:22:21 -05001739 }
1740
John Recke969cc52010-12-21 17:24:43 -08001741 private DataController.OnQueryUrlIsBookmark mIsBookmarkCallback
1742 = new DataController.OnQueryUrlIsBookmark() {
1743 @Override
1744 public void onQueryUrlIsBookmark(String url, boolean isBookmark) {
1745 if (mCurrentState.mUrl.equals(url)) {
1746 mCurrentState.mIsBookmarkedSite = isBookmark;
1747 mWebViewController.bookmarkedStatusHasChanged(Tab.this);
1748 }
Leon Scroggins1961ed22010-12-07 15:22:21 -05001749 }
John Recke969cc52010-12-21 17:24:43 -08001750 };
Grace Kloba22ac16e2009-10-07 18:00:23 -07001751}