blob: 70028ea32e61dec7a1213aae745a0feca14ed3de [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;
Ben Murdoch1d676b62011-01-17 12:54:24 +000058import android.widget.CheckBox;
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
John Reck30c714c2010-12-16 17:30:34 -080082 public enum LockIcon {
83 LOCK_ICON_UNSECURE,
84 LOCK_ICON_SECURE,
85 LOCK_ICON_MIXED,
86 }
Michael Kolb8233fac2010-10-26 16:08:53 -070087
88 Activity mActivity;
89 private WebViewController mWebViewController;
90
Grace Kloba22ac16e2009-10-07 18:00:23 -070091 // The Geolocation permissions prompt
92 private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
93 // Main WebView wrapper
Michael Kolba713ec82010-11-29 17:27:06 -080094 private View mContainer;
Grace Kloba22ac16e2009-10-07 18:00:23 -070095 // Main WebView
96 private WebView mMainView;
97 // Subwindow container
98 private View mSubViewContainer;
99 // Subwindow WebView
100 private WebView mSubView;
101 // Saved bundle for when we are running low on memory. It contains the
102 // information needed to restore the WebView if the user goes back to the
103 // tab.
104 private Bundle mSavedState;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700105 // 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;
John Reck30c714c2010-12-16 17:30:34 -0800118 // The last reported progress of the current page
119 private int mPageLoadProgress;
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000120 // The time the load started, used to find load page time
121 private long mLoadStartTime;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700122 // Application identifier used to find tabs that another application wants
123 // to reuse.
124 private String mAppId;
125 // Keep the original url around to avoid killing the old WebView if the url
126 // has not changed.
Grace Kloba22ac16e2009-10-07 18:00:23 -0700127 // Error console for the tab
128 private ErrorConsoleView mErrorConsole;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -0500129 // The listener that gets invoked when a download is started from the
130 // mMainView
131 private final DownloadListener mDownloadListener;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500132 // Listener used to know when we move forward or back in the history list.
133 private final WebBackForwardListClient mWebBackForwardListClient;
John Recke969cc52010-12-21 17:24:43 -0800134 private DataController mDataController;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700135
136 // AsyncTask for downloading touch icons
137 DownloadTouchIcon mTouchIconLoader;
138
John Reck30c714c2010-12-16 17:30:34 -0800139 // All the state needed for a page
140 private static class PageState {
141 String mUrl;
142 String mTitle;
143 LockIcon mLockIcon;
144 Bitmap mFavicon;
John Recke969cc52010-12-21 17:24:43 -0800145 Boolean mIsBookmarkedSite = false;
John Reck30c714c2010-12-16 17:30:34 -0800146
147 PageState(Context c, boolean incognito) {
148 if (incognito) {
149 mUrl = "browser:incognito";
150 mTitle = c.getString(R.string.new_incognito_tab);
John Reck30c714c2010-12-16 17:30:34 -0800151 } else {
152 mUrl = "";
153 mTitle = c.getString(R.string.new_tab);
John Reck30c714c2010-12-16 17:30:34 -0800154 }
Justin Hodc6cbb72011-01-24 15:14:54 -0800155 mFavicon = BitmapFactory.decodeResource(
156 c.getResources(), R.drawable.app_web_browser_sm);
John Reck30c714c2010-12-16 17:30:34 -0800157 mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
158 }
159
160 PageState(Context c, boolean incognito, String url, Bitmap favicon) {
161 mUrl = url;
162 mTitle = null;
163 if (URLUtil.isHttpsUrl(url)) {
164 mLockIcon = LockIcon.LOCK_ICON_SECURE;
165 } else {
166 mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
167 }
168 if (favicon != null) {
169 mFavicon = favicon;
170 } else {
Justin Hodc6cbb72011-01-24 15:14:54 -0800171 mFavicon = BitmapFactory.decodeResource(
172 c.getResources(), R.drawable.app_web_browser_sm);
John Reck30c714c2010-12-16 17:30:34 -0800173 }
174 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700175 }
176
John Reck30c714c2010-12-16 17:30:34 -0800177 // The current/loading page's state
178 private PageState mCurrentState;
179
Grace Kloba22ac16e2009-10-07 18:00:23 -0700180 // Used for saving and restoring each Tab
John Reck30c714c2010-12-16 17:30:34 -0800181 // TODO: Figure out who uses what and where
182 // Some of these aren't use in this class, and some are only used in
183 // restoring state but not saving it - FIX THIS
Grace Kloba22ac16e2009-10-07 18:00:23 -0700184 static final String WEBVIEW = "webview";
185 static final String NUMTABS = "numTabs";
186 static final String CURRTAB = "currentTab";
187 static final String CURRURL = "currentUrl";
188 static final String CURRTITLE = "currentTitle";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700189 static final String CLOSEONEXIT = "closeonexit";
190 static final String PARENTTAB = "parentTab";
191 static final String APPID = "appid";
192 static final String ORIGINALURL = "originalUrl";
Elliott Slaughter3d6df162010-08-25 13:17:44 -0700193 static final String INCOGNITO = "privateBrowsingEnabled";
Grace Kloba22ac16e2009-10-07 18:00:23 -0700194
195 // -------------------------------------------------------------------------
196
Leon Scroggins58d56c62010-01-28 15:12:40 -0500197 /**
198 * Private information regarding the latest voice search. If the Tab is not
199 * in voice search mode, this will be null.
200 */
201 private VoiceSearchData mVoiceSearchData;
202 /**
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400203 * Remove voice search mode from this tab.
204 */
205 public void revertVoiceSearchMode() {
206 if (mVoiceSearchData != null) {
207 mVoiceSearchData = null;
208 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700209 mWebViewController.revertVoiceSearchMode(this);
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400210 }
211 }
212 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700213
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400214 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500215 * Return whether the tab is in voice search mode.
216 */
217 public boolean isInVoiceSearchMode() {
218 return mVoiceSearchData != null;
219 }
220 /**
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400221 * Return true if the Tab is in voice search mode and the voice search
222 * Intent came with a String identifying that Google provided the Intent.
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500223 */
224 public boolean voiceSearchSourceIsGoogle() {
225 return mVoiceSearchData != null && mVoiceSearchData.mSourceIsGoogle;
226 }
227 /**
Leon Scroggins58d56c62010-01-28 15:12:40 -0500228 * Get the title to display for the current voice search page. If the Tab
229 * is not in voice search mode, return null.
230 */
231 public String getVoiceDisplayTitle() {
232 if (mVoiceSearchData == null) return null;
233 return mVoiceSearchData.mLastVoiceSearchTitle;
234 }
235 /**
236 * Get the latest array of voice search results, to be passed to the
237 * BrowserProvider. If the Tab is not in voice search mode, return null.
238 */
239 public ArrayList<String> getVoiceSearchResults() {
240 if (mVoiceSearchData == null) return null;
241 return mVoiceSearchData.mVoiceSearchResults;
242 }
243 /**
244 * Activate voice search mode.
245 * @param intent Intent which has the results to use, or an index into the
246 * results when reusing the old results.
247 */
248 /* package */ void activateVoiceSearchMode(Intent intent) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500249 int index = 0;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500250 ArrayList<String> results = intent.getStringArrayListExtra(
Leon Scrogginsa1cc3fd2010-02-01 16:14:11 -0500251 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_STRINGS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500252 if (results != null) {
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500253 ArrayList<String> urls = intent.getStringArrayListExtra(
254 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_URLS);
255 ArrayList<String> htmls = intent.getStringArrayListExtra(
256 RecognizerResultsIntent.EXTRA_VOICE_SEARCH_RESULT_HTML);
257 ArrayList<String> baseUrls = intent.getStringArrayListExtra(
258 RecognizerResultsIntent
259 .EXTRA_VOICE_SEARCH_RESULT_HTML_BASE_URLS);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500260 // This tab is now entering voice search mode for the first time, or
261 // a new voice search was done.
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500262 int size = results.size();
263 if (urls == null || size != urls.size()) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500264 throw new AssertionError("improper extras passed in Intent");
265 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500266 if (htmls == null || htmls.size() != size || baseUrls == null ||
267 (baseUrls.size() != size && baseUrls.size() != 1)) {
268 // If either of these arrays are empty/incorrectly sized, ignore
269 // them.
270 htmls = null;
271 baseUrls = null;
272 }
273 mVoiceSearchData = new VoiceSearchData(results, urls, htmls,
274 baseUrls);
Leon Scroggins9df94972010-03-08 18:20:35 -0500275 mVoiceSearchData.mHeaders = intent.getParcelableArrayListExtra(
276 RecognizerResultsIntent
277 .EXTRA_VOICE_SEARCH_RESULT_HTTP_HEADERS);
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500278 mVoiceSearchData.mSourceIsGoogle = intent.getBooleanExtra(
279 VoiceSearchData.SOURCE_IS_GOOGLE, false);
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400280 mVoiceSearchData.mVoiceSearchIntent = new Intent(intent);
Leon Scrogginse10dde52010-03-08 19:53:03 -0500281 }
282 String extraData = intent.getStringExtra(
283 SearchManager.EXTRA_DATA_KEY);
284 if (extraData != null) {
285 index = Integer.parseInt(extraData);
286 if (index >= mVoiceSearchData.mVoiceSearchResults.size()) {
287 throw new AssertionError("index must be less than "
288 + "size of mVoiceSearchResults");
289 }
290 if (mVoiceSearchData.mSourceIsGoogle) {
291 Intent logIntent = new Intent(
292 LoggingEvents.ACTION_LOG_EVENT);
293 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
294 LoggingEvents.VoiceSearch.N_BEST_CHOOSE);
295 logIntent.putExtra(
296 LoggingEvents.VoiceSearch.EXTRA_N_BEST_CHOOSE_INDEX,
297 index);
298 mActivity.sendBroadcast(logIntent);
299 }
300 if (mVoiceSearchData.mVoiceSearchIntent != null) {
Leon Scroggins2ee4a5a2010-03-15 16:56:57 -0400301 // Copy the Intent, so that each history item will have its own
302 // Intent, with different (or none) extra data.
303 Intent latest = new Intent(mVoiceSearchData.mVoiceSearchIntent);
304 latest.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
305 mVoiceSearchData.mVoiceSearchIntent = latest;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500306 }
307 }
308 mVoiceSearchData.mLastVoiceSearchTitle
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500309 = mVoiceSearchData.mVoiceSearchResults.get(index);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500310 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700311 mWebViewController.activateVoiceSearchMode(mVoiceSearchData.mLastVoiceSearchTitle);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500312 }
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500313 if (mVoiceSearchData.mVoiceSearchHtmls != null) {
314 // When index was found it was already ensured that it was valid
315 String uriString = mVoiceSearchData.mVoiceSearchHtmls.get(index);
316 if (uriString != null) {
317 Uri dataUri = Uri.parse(uriString);
318 if (RecognizerResultsIntent.URI_SCHEME_INLINE.equals(
319 dataUri.getScheme())) {
320 // If there is only one base URL, use it. If there are
321 // more, there will be one for each index, so use the base
322 // URL corresponding to the index.
323 String baseUrl = mVoiceSearchData.mVoiceSearchBaseUrls.get(
324 mVoiceSearchData.mVoiceSearchBaseUrls.size() > 1 ?
325 index : 0);
326 mVoiceSearchData.mLastVoiceSearchUrl = baseUrl;
327 mMainView.loadDataWithBaseURL(baseUrl,
328 uriString.substring(RecognizerResultsIntent
329 .URI_SCHEME_INLINE.length() + 1), "text/html",
330 "utf-8", baseUrl);
331 return;
332 }
333 }
334 }
Leon Scroggins58d56c62010-01-28 15:12:40 -0500335 mVoiceSearchData.mLastVoiceSearchUrl
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500336 = mVoiceSearchData.mVoiceSearchUrls.get(index);
337 if (null == mVoiceSearchData.mLastVoiceSearchUrl) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700338 mVoiceSearchData.mLastVoiceSearchUrl = UrlUtils.smartUrlFilter(
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500339 mVoiceSearchData.mLastVoiceSearchTitle);
340 }
Leon Scroggins9df94972010-03-08 18:20:35 -0500341 Map<String, String> headers = null;
342 if (mVoiceSearchData.mHeaders != null) {
343 int bundleIndex = mVoiceSearchData.mHeaders.size() == 1 ? 0
344 : index;
345 Bundle bundle = mVoiceSearchData.mHeaders.get(bundleIndex);
346 if (bundle != null && !bundle.isEmpty()) {
347 Iterator<String> iter = bundle.keySet().iterator();
348 headers = new HashMap<String, String>();
349 while (iter.hasNext()) {
350 String key = iter.next();
351 headers.put(key, bundle.getString(key));
352 }
353 }
354 }
355 mMainView.loadUrl(mVoiceSearchData.mLastVoiceSearchUrl, headers);
Leon Scroggins58d56c62010-01-28 15:12:40 -0500356 }
357 /* package */ static class VoiceSearchData {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500358 public VoiceSearchData(ArrayList<String> results,
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500359 ArrayList<String> urls, ArrayList<String> htmls,
360 ArrayList<String> baseUrls) {
Leon Scroggins58d56c62010-01-28 15:12:40 -0500361 mVoiceSearchResults = results;
362 mVoiceSearchUrls = urls;
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500363 mVoiceSearchHtmls = htmls;
364 mVoiceSearchBaseUrls = baseUrls;
Leon Scroggins58d56c62010-01-28 15:12:40 -0500365 }
366 /*
367 * ArrayList of suggestions to be displayed when opening the
368 * SearchManager
369 */
370 public ArrayList<String> mVoiceSearchResults;
371 /*
372 * ArrayList of urls, associated with the suggestions in
373 * mVoiceSearchResults.
374 */
375 public ArrayList<String> mVoiceSearchUrls;
376 /*
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500377 * ArrayList holding content to load for each item in
378 * mVoiceSearchResults.
379 */
380 public ArrayList<String> mVoiceSearchHtmls;
381 /*
382 * ArrayList holding base urls for the items in mVoiceSearchResults.
383 * If non null, this will either have the same size as
384 * mVoiceSearchResults or have a size of 1, in which case all will use
385 * the same base url
386 */
387 public ArrayList<String> mVoiceSearchBaseUrls;
388 /*
Leon Scroggins58d56c62010-01-28 15:12:40 -0500389 * The last url provided by voice search. Used for comparison to see if
Leon Scroggins82c1baa2010-02-02 16:10:57 -0500390 * we are going to a page by some method besides voice search.
Leon Scroggins58d56c62010-01-28 15:12:40 -0500391 */
392 public String mLastVoiceSearchUrl;
393 /**
394 * The last title used for voice search. Needed to update the title bar
395 * when switching tabs.
396 */
397 public String mLastVoiceSearchTitle;
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500398 /**
399 * Whether the Intent which turned on voice search mode contained the
400 * String signifying that Google was the source.
401 */
402 public boolean mSourceIsGoogle;
403 /**
Leon Scroggins9df94972010-03-08 18:20:35 -0500404 * List of headers to be passed into the WebView containing location
405 * information
406 */
407 public ArrayList<Bundle> mHeaders;
408 /**
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500409 * The Intent used to invoke voice search. Placed on the
410 * WebHistoryItem so that when coming back to a previous voice search
411 * page we can again activate voice search.
412 */
Leon Scrogginse10dde52010-03-08 19:53:03 -0500413 public Intent mVoiceSearchIntent;
Leon Scroggins0c75a8e2010-03-03 16:40:58 -0500414 /**
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500415 * String used to identify Google as the source of voice search.
416 */
417 public static String SOURCE_IS_GOOGLE
418 = "android.speech.extras.SOURCE_IS_GOOGLE";
Leon Scroggins58d56c62010-01-28 15:12:40 -0500419 }
420
Grace Kloba22ac16e2009-10-07 18:00:23 -0700421 // Container class for the next error dialog that needs to be displayed
422 private class ErrorDialog {
423 public final int mTitle;
424 public final String mDescription;
425 public final int mError;
426 ErrorDialog(int title, String desc, int error) {
427 mTitle = title;
428 mDescription = desc;
429 mError = error;
430 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700431 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700432
433 private void processNextError() {
434 if (mQueuedErrors == null) {
435 return;
436 }
437 // The first one is currently displayed so just remove it.
438 mQueuedErrors.removeFirst();
439 if (mQueuedErrors.size() == 0) {
440 mQueuedErrors = null;
441 return;
442 }
443 showError(mQueuedErrors.getFirst());
444 }
445
446 private DialogInterface.OnDismissListener mDialogListener =
447 new DialogInterface.OnDismissListener() {
448 public void onDismiss(DialogInterface d) {
449 processNextError();
450 }
451 };
452 private LinkedList<ErrorDialog> mQueuedErrors;
453
454 private void queueError(int err, String desc) {
455 if (mQueuedErrors == null) {
456 mQueuedErrors = new LinkedList<ErrorDialog>();
457 }
458 for (ErrorDialog d : mQueuedErrors) {
459 if (d.mError == err) {
460 // Already saw a similar error, ignore the new one.
461 return;
462 }
463 }
464 ErrorDialog errDialog = new ErrorDialog(
465 err == WebViewClient.ERROR_FILE_NOT_FOUND ?
466 R.string.browserFrameFileErrorLabel :
467 R.string.browserFrameNetworkErrorLabel,
468 desc, err);
469 mQueuedErrors.addLast(errDialog);
470
471 // Show the dialog now if the queue was empty and it is in foreground
472 if (mQueuedErrors.size() == 1 && mInForeground) {
473 showError(errDialog);
474 }
475 }
476
477 private void showError(ErrorDialog errDialog) {
478 if (mInForeground) {
479 AlertDialog d = new AlertDialog.Builder(mActivity)
480 .setTitle(errDialog.mTitle)
481 .setMessage(errDialog.mDescription)
482 .setPositiveButton(R.string.ok, null)
483 .create();
484 d.setOnDismissListener(mDialogListener);
485 d.show();
486 }
487 }
488
489 // -------------------------------------------------------------------------
490 // WebViewClient implementation for the main WebView
491 // -------------------------------------------------------------------------
492
493 private final WebViewClient mWebViewClient = new WebViewClient() {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500494 private Message mDontResend;
495 private Message mResend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700496 @Override
497 public void onPageStarted(WebView view, String url, Bitmap favicon) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700498 mInPageLoad = true;
John Reck30c714c2010-12-16 17:30:34 -0800499 mPageLoadProgress = 0;
500 mCurrentState = new PageState(mActivity,
501 view.isPrivateBrowsingEnabled(), url, favicon);
Kristian Monsen4dce3bf2010-02-02 13:37:09 +0000502 mLoadStartTime = SystemClock.uptimeMillis();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500503 if (mVoiceSearchData != null
504 && !url.equals(mVoiceSearchData.mLastVoiceSearchUrl)) {
Leon Scroggins1fe13a52010-02-09 15:31:26 -0500505 if (mVoiceSearchData.mSourceIsGoogle) {
506 Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
507 i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
508 mActivity.sendBroadcast(i);
509 }
Leon Scroggins III95d9bfd2010-09-14 14:02:36 -0400510 revertVoiceSearchMode();
Leon Scroggins58d56c62010-01-28 15:12:40 -0500511 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700512
Grace Kloba22ac16e2009-10-07 18:00:23 -0700513
514 // If we start a touch icon load and then load a new page, we don't
515 // want to cancel the current touch icon loader. But, we do want to
516 // create a new one when the touch icon url is known.
517 if (mTouchIconLoader != null) {
518 mTouchIconLoader.mTab = null;
519 mTouchIconLoader = null;
520 }
521
522 // reset the error console
523 if (mErrorConsole != null) {
524 mErrorConsole.clearErrorMessages();
Michael Kolb8233fac2010-10-26 16:08:53 -0700525 if (mWebViewController.shouldShowErrorConsole()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700526 mErrorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
527 }
528 }
529
Grace Kloba22ac16e2009-10-07 18:00:23 -0700530 // finally update the UI in the activity if it is in the foreground
John Reck324d4402011-01-11 16:56:42 -0800531 mWebViewController.onPageStarted(Tab.this, view, favicon);
Leon Scroggins4cd97792010-12-03 15:31:56 -0500532
John Recke969cc52010-12-21 17:24:43 -0800533 updateBookmarkedStatus();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700534 }
535
536 @Override
537 public void onPageFinished(WebView view, String url) {
John Reck5b691842010-11-29 11:21:13 -0800538 if (!isPrivateBrowsingEnabled()) {
539 LogTag.logPageFinishedLoading(
540 url, SystemClock.uptimeMillis() - mLoadStartTime);
541 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700542 mInPageLoad = false;
John Reck30c714c2010-12-16 17:30:34 -0800543 // Sync state (in case of stop/timeout)
544 mCurrentState.mUrl = view.getUrl();
John Reck6c702ee2011-01-07 09:41:53 -0800545 if (mCurrentState.mUrl == null) {
546 mCurrentState.mUrl = url != null ? url : "";
547 }
John Reck30c714c2010-12-16 17:30:34 -0800548 mCurrentState.mTitle = view.getTitle();
549 mCurrentState.mFavicon = view.getFavicon();
550 if (!URLUtil.isHttpsUrl(mCurrentState.mUrl)) {
551 // In case we stop when loading an HTTPS page from an HTTP page
552 // but before a provisional load occurred
553 mCurrentState.mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
554 }
John Reck324d4402011-01-11 16:56:42 -0800555 mWebViewController.onPageFinished(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700556 }
557
558 // return true if want to hijack the url to let another app to handle it
559 @Override
560 public boolean shouldOverrideUrlLoading(WebView view, String url) {
Leon Scroggins IIIc1f5ae22010-06-29 17:11:29 -0400561 if (voiceSearchSourceIsGoogle()) {
562 // This method is called when the user clicks on a link.
563 // VoiceSearchMode is turned off when the user leaves the
564 // Google results page, so at this point the user must be on
565 // that page. If the user clicked a link on that page, assume
566 // that the voice search was effective, and broadcast an Intent
567 // so a receiver can take note of that fact.
568 Intent logIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
569 logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
570 LoggingEvents.VoiceSearch.RESULT_CLICKED);
571 mActivity.sendBroadcast(logIntent);
572 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700573 if (mInForeground) {
Michael Kolb18eb3772010-12-10 14:29:51 -0800574 return mWebViewController.shouldOverrideUrlLoading(Tab.this,
575 view, url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700576 } else {
577 return false;
578 }
579 }
580
581 /**
582 * Updates the lock icon. This method is called when we discover another
583 * resource to be loaded for this page (for example, javascript). While
584 * we update the icon type, we do not update the lock icon itself until
585 * we are done loading, it is slightly more secure this way.
586 */
587 @Override
588 public void onLoadResource(WebView view, String url) {
589 if (url != null && url.length() > 0) {
590 // It is only if the page claims to be secure that we may have
591 // to update the lock:
John Reck30c714c2010-12-16 17:30:34 -0800592 if (mCurrentState.mLockIcon == LockIcon.LOCK_ICON_SECURE) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700593 // If NOT a 'safe' url, change the lock to mixed content!
594 if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url)
595 || URLUtil.isAboutUrl(url))) {
John Reck30c714c2010-12-16 17:30:34 -0800596 mCurrentState.mLockIcon = LockIcon.LOCK_ICON_MIXED;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700597 }
598 }
599 }
600 }
601
602 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -0700603 * Show a dialog informing the user of the network error reported by
604 * WebCore if it is in the foreground.
605 */
606 @Override
607 public void onReceivedError(WebView view, int errorCode,
608 String description, String failingUrl) {
609 if (errorCode != WebViewClient.ERROR_HOST_LOOKUP &&
610 errorCode != WebViewClient.ERROR_CONNECT &&
611 errorCode != WebViewClient.ERROR_BAD_URL &&
612 errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME &&
613 errorCode != WebViewClient.ERROR_FILE) {
614 queueError(errorCode, description);
615 }
Jeff Hamilton47654f42010-09-07 09:57:51 -0500616
617 // Don't log URLs when in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -0700618 if (!isPrivateBrowsingEnabled()) {
Jeff Hamilton47654f42010-09-07 09:57:51 -0500619 Log.e(LOGTAG, "onReceivedError " + errorCode + " " + failingUrl
620 + " " + description);
621 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700622 }
623
624 /**
625 * Check with the user if it is ok to resend POST data as the page they
626 * are trying to navigate to is the result of a POST.
627 */
628 @Override
629 public void onFormResubmission(WebView view, final Message dontResend,
630 final Message resend) {
631 if (!mInForeground) {
632 dontResend.sendToTarget();
633 return;
634 }
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500635 if (mDontResend != null) {
636 Log.w(LOGTAG, "onFormResubmission should not be called again "
637 + "while dialog is still up");
638 dontResend.sendToTarget();
639 return;
640 }
641 mDontResend = dontResend;
642 mResend = resend;
Grace Kloba22ac16e2009-10-07 18:00:23 -0700643 new AlertDialog.Builder(mActivity).setTitle(
644 R.string.browserFrameFormResubmitLabel).setMessage(
645 R.string.browserFrameFormResubmitMessage)
646 .setPositiveButton(R.string.ok,
647 new DialogInterface.OnClickListener() {
648 public void onClick(DialogInterface dialog,
649 int which) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500650 if (mResend != null) {
651 mResend.sendToTarget();
652 mResend = null;
653 mDontResend = null;
654 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700655 }
656 }).setNegativeButton(R.string.cancel,
657 new DialogInterface.OnClickListener() {
658 public void onClick(DialogInterface dialog,
659 int which) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500660 if (mDontResend != null) {
661 mDontResend.sendToTarget();
662 mResend = null;
663 mDontResend = null;
664 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700665 }
666 }).setOnCancelListener(new OnCancelListener() {
667 public void onCancel(DialogInterface dialog) {
Leon Scroggins4a64a8a2010-03-02 17:57:40 -0500668 if (mDontResend != null) {
669 mDontResend.sendToTarget();
670 mResend = null;
671 mDontResend = null;
672 }
Grace Kloba22ac16e2009-10-07 18:00:23 -0700673 }
674 }).show();
675 }
676
677 /**
678 * Insert the url into the visited history database.
679 * @param url The url to be inserted.
680 * @param isReload True if this url is being reloaded.
681 * FIXME: Not sure what to do when reloading the page.
682 */
683 @Override
684 public void doUpdateVisitedHistory(WebView view, String url,
685 boolean isReload) {
John Reck324d4402011-01-11 16:56:42 -0800686 mWebViewController.doUpdateVisitedHistory(Tab.this, isReload);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700687 }
688
689 /**
690 * Displays SSL error(s) dialog to the user.
691 */
692 @Override
693 public void onReceivedSslError(final WebView view,
694 final SslErrorHandler handler, final SslError error) {
695 if (!mInForeground) {
696 handler.cancel();
John Reck30c714c2010-12-16 17:30:34 -0800697 setLockIconType(LockIcon.LOCK_ICON_UNSECURE);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700698 return;
699 }
700 if (BrowserSettings.getInstance().showSecurityWarnings()) {
701 final LayoutInflater factory =
702 LayoutInflater.from(mActivity);
703 final View warningsView =
704 factory.inflate(R.layout.ssl_warnings, null);
705 final LinearLayout placeholder =
706 (LinearLayout)warningsView.findViewById(R.id.placeholder);
707
708 if (error.hasError(SslError.SSL_UNTRUSTED)) {
709 LinearLayout ll = (LinearLayout)factory
710 .inflate(R.layout.ssl_warning, null);
711 ((TextView)ll.findViewById(R.id.warning))
712 .setText(R.string.ssl_untrusted);
713 placeholder.addView(ll);
714 }
715
716 if (error.hasError(SslError.SSL_IDMISMATCH)) {
717 LinearLayout ll = (LinearLayout)factory
718 .inflate(R.layout.ssl_warning, null);
719 ((TextView)ll.findViewById(R.id.warning))
720 .setText(R.string.ssl_mismatch);
721 placeholder.addView(ll);
722 }
723
724 if (error.hasError(SslError.SSL_EXPIRED)) {
725 LinearLayout ll = (LinearLayout)factory
726 .inflate(R.layout.ssl_warning, null);
727 ((TextView)ll.findViewById(R.id.warning))
728 .setText(R.string.ssl_expired);
729 placeholder.addView(ll);
730 }
731
732 if (error.hasError(SslError.SSL_NOTYETVALID)) {
733 LinearLayout ll = (LinearLayout)factory
734 .inflate(R.layout.ssl_warning, null);
735 ((TextView)ll.findViewById(R.id.warning))
736 .setText(R.string.ssl_not_yet_valid);
737 placeholder.addView(ll);
738 }
739
740 new AlertDialog.Builder(mActivity).setTitle(
741 R.string.security_warning).setIcon(
742 android.R.drawable.ic_dialog_alert).setView(
743 warningsView).setPositiveButton(R.string.ssl_continue,
744 new DialogInterface.OnClickListener() {
745 public void onClick(DialogInterface dialog,
746 int whichButton) {
747 handler.proceed();
748 }
749 }).setNeutralButton(R.string.view_certificate,
750 new DialogInterface.OnClickListener() {
751 public void onClick(DialogInterface dialog,
752 int whichButton) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700753 mWebViewController.showSslCertificateOnError(view,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700754 handler, error);
755 }
Ben Murdocha49b8292010-11-16 11:56:04 +0000756 }).setNegativeButton(R.string.ssl_go_back,
Grace Kloba22ac16e2009-10-07 18:00:23 -0700757 new DialogInterface.OnClickListener() {
758 public void onClick(DialogInterface dialog,
759 int whichButton) {
John Reck30c714c2010-12-16 17:30:34 -0800760 dialog.cancel();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700761 }
762 }).setOnCancelListener(
763 new DialogInterface.OnCancelListener() {
764 public void onCancel(DialogInterface dialog) {
765 handler.cancel();
John Reck30c714c2010-12-16 17:30:34 -0800766 setLockIconType(LockIcon.LOCK_ICON_UNSECURE);
767 mWebViewController.onUserCanceledSsl(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700768 }
769 }).show();
770 } else {
771 handler.proceed();
772 }
773 }
774
775 /**
776 * Handles an HTTP authentication request.
777 *
778 * @param handler The authentication handler
779 * @param host The host
780 * @param realm The realm
781 */
782 @Override
783 public void onReceivedHttpAuthRequest(WebView view,
784 final HttpAuthHandler handler, final String host,
785 final String realm) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700786 mWebViewController.onReceivedHttpAuthRequest(Tab.this, view, handler, host, realm);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700787 }
788
789 @Override
John Reck438bf462011-01-12 18:11:46 -0800790 public WebResourceResponse shouldInterceptRequest(WebView view,
791 String url) {
792 WebResourceResponse res = HomeProvider.shouldInterceptRequest(
793 mActivity, url);
794 return res;
795 }
796
797 @Override
Grace Kloba22ac16e2009-10-07 18:00:23 -0700798 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
799 if (!mInForeground) {
800 return false;
801 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700802 return mWebViewController.shouldOverrideKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700803 }
804
805 @Override
806 public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700807 if (!mInForeground) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700808 return;
809 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700810 mWebViewController.onUnhandledKeyEvent(event);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700811 }
812 };
813
814 // -------------------------------------------------------------------------
815 // WebChromeClient implementation for the main WebView
816 // -------------------------------------------------------------------------
817
818 private final WebChromeClient mWebChromeClient = new WebChromeClient() {
819 // Helper method to create a new tab or sub window.
820 private void createWindow(final boolean dialog, final Message msg) {
821 WebView.WebViewTransport transport =
822 (WebView.WebViewTransport) msg.obj;
823 if (dialog) {
824 createSubWindow();
Michael Kolb8233fac2010-10-26 16:08:53 -0700825 mWebViewController.attachSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700826 transport.setWebView(mSubView);
827 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -0700828 final Tab newTab = mWebViewController.openTabAndShow(
Michael Kolb18eb3772010-12-10 14:29:51 -0800829 Tab.this,
Michael Kolb8233fac2010-10-26 16:08:53 -0700830 IntentHandler.EMPTY_URL_DATA, false, null);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700831 if (newTab != Tab.this) {
832 Tab.this.addChildTab(newTab);
833 }
834 transport.setWebView(newTab.getWebView());
835 }
836 msg.sendToTarget();
837 }
838
839 @Override
840 public boolean onCreateWindow(WebView view, final boolean dialog,
841 final boolean userGesture, final Message resultMsg) {
842 // only allow new window or sub window for the foreground case
843 if (!mInForeground) {
844 return false;
845 }
846 // Short-circuit if we can't create any more tabs or sub windows.
847 if (dialog && mSubView != null) {
848 new AlertDialog.Builder(mActivity)
849 .setTitle(R.string.too_many_subwindows_dialog_title)
850 .setIcon(android.R.drawable.ic_dialog_alert)
851 .setMessage(R.string.too_many_subwindows_dialog_message)
852 .setPositiveButton(R.string.ok, null)
853 .show();
854 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700855 } else if (!mWebViewController.getTabControl().canCreateNewTab()) {
Grace Kloba22ac16e2009-10-07 18:00:23 -0700856 new AlertDialog.Builder(mActivity)
857 .setTitle(R.string.too_many_windows_dialog_title)
858 .setIcon(android.R.drawable.ic_dialog_alert)
859 .setMessage(R.string.too_many_windows_dialog_message)
860 .setPositiveButton(R.string.ok, null)
861 .show();
862 return false;
863 }
864
865 // Short-circuit if this was a user gesture.
866 if (userGesture) {
867 createWindow(dialog, resultMsg);
868 return true;
869 }
870
871 // Allow the popup and create the appropriate window.
872 final AlertDialog.OnClickListener allowListener =
873 new AlertDialog.OnClickListener() {
874 public void onClick(DialogInterface d,
875 int which) {
876 createWindow(dialog, resultMsg);
877 }
878 };
879
880 // Block the popup by returning a null WebView.
881 final AlertDialog.OnClickListener blockListener =
882 new AlertDialog.OnClickListener() {
883 public void onClick(DialogInterface d, int which) {
884 resultMsg.sendToTarget();
885 }
886 };
887
888 // Build a confirmation dialog to display to the user.
889 final AlertDialog d =
890 new AlertDialog.Builder(mActivity)
891 .setTitle(R.string.attention)
892 .setIcon(android.R.drawable.ic_dialog_alert)
893 .setMessage(R.string.popup_window_attempt)
894 .setPositiveButton(R.string.allow, allowListener)
895 .setNegativeButton(R.string.block, blockListener)
896 .setCancelable(false)
897 .create();
898
899 // Show the confirmation dialog.
900 d.show();
901 return true;
902 }
903
904 @Override
Patrick Scotteb5061b2009-11-18 15:00:30 -0500905 public void onRequestFocus(WebView view) {
906 if (!mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700907 mWebViewController.switchToTab(mWebViewController.getTabControl().getTabIndex(
Patrick Scotteb5061b2009-11-18 15:00:30 -0500908 Tab.this));
909 }
910 }
911
912 @Override
Grace Kloba22ac16e2009-10-07 18:00:23 -0700913 public void onCloseWindow(WebView window) {
914 if (mParentTab != null) {
915 // JavaScript can only close popup window.
916 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700917 mWebViewController.switchToTab(mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -0700918 .getTabIndex(mParentTab));
919 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700920 mWebViewController.closeTab(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700921 }
922 }
923
924 @Override
925 public void onProgressChanged(WebView view, int newProgress) {
John Reck30c714c2010-12-16 17:30:34 -0800926 mPageLoadProgress = newProgress;
927 mWebViewController.onProgressChanged(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700928 }
929
930 @Override
Leon Scroggins21d9b902010-03-11 09:33:11 -0500931 public void onReceivedTitle(WebView view, final String title) {
John Reck30c714c2010-12-16 17:30:34 -0800932 mCurrentState.mTitle = title;
Michael Kolb8233fac2010-10-26 16:08:53 -0700933 mWebViewController.onReceivedTitle(Tab.this, title);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700934 }
935
936 @Override
937 public void onReceivedIcon(WebView view, Bitmap icon) {
John Reck30c714c2010-12-16 17:30:34 -0800938 mCurrentState.mFavicon = icon;
Michael Kolb8233fac2010-10-26 16:08:53 -0700939 mWebViewController.onFavicon(Tab.this, view, icon);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700940 }
941
942 @Override
943 public void onReceivedTouchIconUrl(WebView view, String url,
944 boolean precomposed) {
945 final ContentResolver cr = mActivity.getContentResolver();
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400946 // Let precomposed icons take precedence over non-composed
947 // icons.
948 if (precomposed && mTouchIconLoader != null) {
949 mTouchIconLoader.cancel(false);
950 mTouchIconLoader = null;
951 }
952 // Have only one async task at a time.
953 if (mTouchIconLoader == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700954 mTouchIconLoader = new DownloadTouchIcon(Tab.this,
955 mActivity, cr, view);
Leon Scrogginsc8393d92010-04-23 14:58:16 -0400956 mTouchIconLoader.execute(url);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700957 }
958 }
959
960 @Override
961 public void onShowCustomView(View view,
962 WebChromeClient.CustomViewCallback callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700963 if (mInForeground) mWebViewController.showCustomView(Tab.this, view,
964 callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -0700965 }
966
967 @Override
968 public void onHideCustomView() {
Michael Kolb8233fac2010-10-26 16:08:53 -0700969 if (mInForeground) mWebViewController.hideCustomView();
Grace Kloba22ac16e2009-10-07 18:00:23 -0700970 }
971
972 /**
973 * The origin has exceeded its database quota.
974 * @param url the URL that exceeded the quota
975 * @param databaseIdentifier the identifier of the database on which the
976 * transaction that caused the quota overflow was run
977 * @param currentQuota the current quota for the origin.
978 * @param estimatedSize the estimated size of the database.
979 * @param totalUsedQuota is the sum of all origins' quota.
980 * @param quotaUpdater The callback to run when a decision to allow or
981 * deny quota has been made. Don't forget to call this!
982 */
983 @Override
984 public void onExceededDatabaseQuota(String url,
985 String databaseIdentifier, long currentQuota, long estimatedSize,
986 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
987 BrowserSettings.getInstance().getWebStorageSizeManager()
988 .onExceededDatabaseQuota(url, databaseIdentifier,
989 currentQuota, estimatedSize, totalUsedQuota,
990 quotaUpdater);
991 }
992
993 /**
994 * The Application Cache has exceeded its max size.
995 * @param spaceNeeded is the amount of disk space that would be needed
996 * in order for the last appcache operation to succeed.
997 * @param totalUsedQuota is the sum of all origins' quota.
998 * @param quotaUpdater A callback to inform the WebCore thread that a
999 * new app cache size is available. This callback must always
1000 * be executed at some point to ensure that the sleeping
1001 * WebCore thread is woken up.
1002 */
1003 @Override
1004 public void onReachedMaxAppCacheSize(long spaceNeeded,
1005 long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
1006 BrowserSettings.getInstance().getWebStorageSizeManager()
1007 .onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
1008 quotaUpdater);
1009 }
1010
1011 /**
1012 * Instructs the browser to show a prompt to ask the user to set the
1013 * Geolocation permission state for the specified origin.
1014 * @param origin The origin for which Geolocation permissions are
1015 * requested.
1016 * @param callback The callback to call once the user has set the
1017 * Geolocation permission state.
1018 */
1019 @Override
1020 public void onGeolocationPermissionsShowPrompt(String origin,
1021 GeolocationPermissions.Callback callback) {
1022 if (mInForeground) {
Grace Kloba50c241e2010-04-20 11:07:50 -07001023 getGeolocationPermissionsPrompt().show(origin, callback);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001024 }
1025 }
1026
1027 /**
1028 * Instructs the browser to hide the Geolocation permissions prompt.
1029 */
1030 @Override
1031 public void onGeolocationPermissionsHidePrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001032 if (mInForeground && mGeolocationPermissionsPrompt != null) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001033 mGeolocationPermissionsPrompt.hide();
1034 }
1035 }
1036
Ben Murdoch65acc352009-11-19 18:16:04 +00001037 /* Adds a JavaScript error message to the system log and if the JS
1038 * console is enabled in the about:debug options, to that console
1039 * also.
Ben Murdochc42addf2010-01-28 15:19:59 +00001040 * @param consoleMessage the message object.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001041 */
1042 @Override
Ben Murdochc42addf2010-01-28 15:19:59 +00001043 public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001044 if (mInForeground) {
1045 // call getErrorConsole(true) so it will create one if needed
1046 ErrorConsoleView errorConsole = getErrorConsole(true);
Ben Murdochc42addf2010-01-28 15:19:59 +00001047 errorConsole.addErrorMessage(consoleMessage);
Michael Kolb8233fac2010-10-26 16:08:53 -07001048 if (mWebViewController.shouldShowErrorConsole()
1049 && errorConsole.getShowState() !=
1050 ErrorConsoleView.SHOW_MAXIMIZED) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001051 errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED);
1052 }
1053 }
Ben Murdochc42addf2010-01-28 15:19:59 +00001054
Jeff Hamilton47654f42010-09-07 09:57:51 -05001055 // Don't log console messages in private browsing mode
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001056 if (isPrivateBrowsingEnabled()) return true;
Jeff Hamilton47654f42010-09-07 09:57:51 -05001057
Ben Murdochc42addf2010-01-28 15:19:59 +00001058 String message = "Console: " + consoleMessage.message() + " "
1059 + consoleMessage.sourceId() + ":"
1060 + consoleMessage.lineNumber();
1061
1062 switch (consoleMessage.messageLevel()) {
1063 case TIP:
1064 Log.v(CONSOLE_LOGTAG, message);
1065 break;
1066 case LOG:
1067 Log.i(CONSOLE_LOGTAG, message);
1068 break;
1069 case WARNING:
1070 Log.w(CONSOLE_LOGTAG, message);
1071 break;
1072 case ERROR:
1073 Log.e(CONSOLE_LOGTAG, message);
1074 break;
1075 case DEBUG:
1076 Log.d(CONSOLE_LOGTAG, message);
1077 break;
1078 }
1079
1080 return true;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001081 }
1082
1083 /**
1084 * Ask the browser for an icon to represent a <video> element.
1085 * This icon will be used if the Web page did not specify a poster attribute.
1086 * @return Bitmap The icon or null if no such icon is available.
1087 */
1088 @Override
1089 public Bitmap getDefaultVideoPoster() {
1090 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001091 return mWebViewController.getDefaultVideoPoster();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001092 }
1093 return null;
1094 }
1095
1096 /**
1097 * Ask the host application for a custom progress view to show while
1098 * a <video> is loading.
1099 * @return View The progress view.
1100 */
1101 @Override
1102 public View getVideoLoadingProgressView() {
1103 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001104 return mWebViewController.getVideoLoadingProgressView();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001105 }
1106 return null;
1107 }
1108
1109 @Override
Ben Murdoch62b1b7e2010-05-19 20:38:56 +01001110 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001111 if (mInForeground) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001112 mWebViewController.openFileChooser(uploadMsg, acceptType);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001113 } else {
1114 uploadMsg.onReceiveValue(null);
1115 }
1116 }
1117
1118 /**
1119 * Deliver a list of already-visited URLs
1120 */
1121 @Override
1122 public void getVisitedHistory(final ValueCallback<String[]> callback) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001123 mWebViewController.getVisitedHistory(callback);
1124 }
Ben Murdoch8029a772010-11-16 11:58:21 +00001125
1126 @Override
1127 public void setupAutoFill(Message message) {
1128 // Prompt the user to set up their profile.
1129 final Message msg = message;
1130 AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
Ben Murdoch1d676b62011-01-17 12:54:24 +00001131 LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
1132 Context.LAYOUT_INFLATER_SERVICE);
1133 final View layout = inflater.inflate(R.layout.setup_autofill_dialog, null);
1134
1135 builder.setView(layout)
1136 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
1137 @Override
1138 public void onClick(DialogInterface dialog, int id) {
1139 CheckBox disableAutoFill = (CheckBox) layout.findViewById(
1140 R.id.setup_autofill_dialog_disable_autofill);
1141
1142 if (disableAutoFill.isChecked()) {
1143 // Disable autofill and show a toast with how to turn it on again.
1144 BrowserSettings s = BrowserSettings.getInstance();
1145 s.addObserver(mMainView.getSettings());
1146 s.disableAutoFill(mActivity);
1147 s.update();
1148 Toast.makeText(mActivity,
1149 R.string.autofill_setup_dialog_negative_toast,
1150 Toast.LENGTH_LONG).show();
1151 } else {
1152 // Take user to the AutoFill profile editor. When they return,
1153 // we will send the message that we pass here which will trigger
1154 // the form to get filled out with their new profile.
1155 mWebViewController.setupAutoFill(msg);
1156 }
1157 }
1158 })
1159 .setNegativeButton(R.string.cancel, null)
1160 .show();
Ben Murdoch8029a772010-11-16 11:58:21 +00001161 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001162 };
1163
1164 // -------------------------------------------------------------------------
1165 // WebViewClient implementation for the sub window
1166 // -------------------------------------------------------------------------
1167
1168 // Subclass of WebViewClient used in subwindows to notify the main
1169 // WebViewClient of certain WebView activities.
1170 private static class SubWindowClient extends WebViewClient {
1171 // The main WebViewClient.
1172 private final WebViewClient mClient;
Michael Kolb8233fac2010-10-26 16:08:53 -07001173 private final WebViewController mController;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001174
Michael Kolb8233fac2010-10-26 16:08:53 -07001175 SubWindowClient(WebViewClient client, WebViewController controller) {
Grace Kloba22ac16e2009-10-07 18:00:23 -07001176 mClient = client;
Michael Kolb8233fac2010-10-26 16:08:53 -07001177 mController = controller;
Leon Scroggins III211ba542010-04-19 13:21:13 -04001178 }
1179 @Override
1180 public void onPageStarted(WebView view, String url, Bitmap favicon) {
1181 // Unlike the others, do not call mClient's version, which would
1182 // change the progress bar. However, we do want to remove the
Cary Clark01cfcdd2010-06-04 16:36:45 -04001183 // find or select dialog.
Michael Kolb8233fac2010-10-26 16:08:53 -07001184 mController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001185 }
1186 @Override
1187 public void doUpdateVisitedHistory(WebView view, String url,
1188 boolean isReload) {
1189 mClient.doUpdateVisitedHistory(view, url, isReload);
1190 }
1191 @Override
1192 public boolean shouldOverrideUrlLoading(WebView view, String url) {
1193 return mClient.shouldOverrideUrlLoading(view, url);
1194 }
1195 @Override
1196 public void onReceivedSslError(WebView view, SslErrorHandler handler,
1197 SslError error) {
1198 mClient.onReceivedSslError(view, handler, error);
1199 }
1200 @Override
1201 public void onReceivedHttpAuthRequest(WebView view,
1202 HttpAuthHandler handler, String host, String realm) {
1203 mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
1204 }
1205 @Override
1206 public void onFormResubmission(WebView view, Message dontResend,
1207 Message resend) {
1208 mClient.onFormResubmission(view, dontResend, resend);
1209 }
1210 @Override
1211 public void onReceivedError(WebView view, int errorCode,
1212 String description, String failingUrl) {
1213 mClient.onReceivedError(view, errorCode, description, failingUrl);
1214 }
1215 @Override
1216 public boolean shouldOverrideKeyEvent(WebView view,
1217 android.view.KeyEvent event) {
1218 return mClient.shouldOverrideKeyEvent(view, event);
1219 }
1220 @Override
1221 public void onUnhandledKeyEvent(WebView view,
1222 android.view.KeyEvent event) {
1223 mClient.onUnhandledKeyEvent(view, event);
1224 }
1225 }
1226
1227 // -------------------------------------------------------------------------
1228 // WebChromeClient implementation for the sub window
1229 // -------------------------------------------------------------------------
1230
1231 private class SubWindowChromeClient extends WebChromeClient {
1232 // The main WebChromeClient.
1233 private final WebChromeClient mClient;
1234
1235 SubWindowChromeClient(WebChromeClient client) {
1236 mClient = client;
1237 }
1238 @Override
1239 public void onProgressChanged(WebView view, int newProgress) {
1240 mClient.onProgressChanged(view, newProgress);
1241 }
1242 @Override
1243 public boolean onCreateWindow(WebView view, boolean dialog,
1244 boolean userGesture, android.os.Message resultMsg) {
1245 return mClient.onCreateWindow(view, dialog, userGesture, resultMsg);
1246 }
1247 @Override
1248 public void onCloseWindow(WebView window) {
1249 if (window != mSubView) {
1250 Log.e(LOGTAG, "Can't close the window");
1251 }
Michael Kolb8233fac2010-10-26 16:08:53 -07001252 mWebViewController.dismissSubWindow(Tab.this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001253 }
1254 }
1255
1256 // -------------------------------------------------------------------------
1257
Michael Kolb8233fac2010-10-26 16:08:53 -07001258 // TODO temporarily use activity here
1259 // remove later
1260
Grace Kloba22ac16e2009-10-07 18:00:23 -07001261 // Construct a new tab
Michael Kolb8233fac2010-10-26 16:08:53 -07001262 Tab(WebViewController wvcontroller, WebView w, boolean closeOnExit, String appId,
Grace Kloba22ac16e2009-10-07 18:00:23 -07001263 String url) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001264 mWebViewController = wvcontroller;
1265 mActivity = mWebViewController.getActivity();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001266 mCloseOnExit = closeOnExit;
1267 mAppId = appId;
John Recke969cc52010-12-21 17:24:43 -08001268 mDataController = DataController.getInstance(mActivity);
John Reck30c714c2010-12-16 17:30:34 -08001269 mCurrentState = new PageState(mActivity, w.isPrivateBrowsingEnabled());
Michael Kolb8233fac2010-10-26 16:08:53 -07001270 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001271 mInForeground = false;
1272
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001273 mDownloadListener = new DownloadListener() {
1274 public void onDownloadStart(String url, String userAgent,
1275 String contentDisposition, String mimetype,
1276 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001277 mWebViewController.onDownloadStart(Tab.this, url, userAgent, contentDisposition,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001278 mimetype, contentLength);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001279 }
1280 };
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001281 mWebBackForwardListClient = new WebBackForwardListClient() {
1282 @Override
1283 public void onNewHistoryItem(WebHistoryItem item) {
1284 if (isInVoiceSearchMode()) {
1285 item.setCustomData(mVoiceSearchData.mVoiceSearchIntent);
1286 }
1287 }
1288 @Override
1289 public void onIndexChanged(WebHistoryItem item, int index) {
1290 Object data = item.getCustomData();
1291 if (data != null && data instanceof Intent) {
1292 activateVoiceSearchMode((Intent) data);
1293 }
1294 }
1295 };
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001296
Grace Kloba22ac16e2009-10-07 18:00:23 -07001297 setWebView(w);
1298 }
1299
1300 /**
1301 * Sets the WebView for this tab, correctly removing the old WebView from
1302 * the container view.
1303 */
1304 void setWebView(WebView w) {
1305 if (mMainView == w) {
1306 return;
1307 }
Michael Kolba713ec82010-11-29 17:27:06 -08001308
Grace Kloba22ac16e2009-10-07 18:00:23 -07001309 // If the WebView is changing, the page will be reloaded, so any ongoing
1310 // Geolocation permission requests are void.
Grace Kloba50c241e2010-04-20 11:07:50 -07001311 if (mGeolocationPermissionsPrompt != null) {
1312 mGeolocationPermissionsPrompt.hide();
1313 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001314
Michael Kolba713ec82010-11-29 17:27:06 -08001315 mWebViewController.onSetWebView(this, w);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001316
1317 // set the new one
1318 mMainView = w;
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001319 // attach the WebViewClient, WebChromeClient and DownloadListener
Grace Kloba22ac16e2009-10-07 18:00:23 -07001320 if (mMainView != null) {
1321 mMainView.setWebViewClient(mWebViewClient);
1322 mMainView.setWebChromeClient(mWebChromeClient);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001323 // Attach DownloadManager so that downloads can start in an active
1324 // or a non-active window. This can happen when going to a site that
1325 // does a redirect after a period of time. The user could have
1326 // switched to another tab while waiting for the download to start.
1327 mMainView.setDownloadListener(mDownloadListener);
Leon Scroggins0c75a8e2010-03-03 16:40:58 -05001328 mMainView.setWebBackForwardListClient(mWebBackForwardListClient);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001329 }
1330 }
1331
1332 /**
1333 * Destroy the tab's main WebView and subWindow if any
1334 */
1335 void destroy() {
1336 if (mMainView != null) {
1337 dismissSubWindow();
1338 BrowserSettings.getInstance().deleteObserver(mMainView.getSettings());
1339 // save the WebView to call destroy() after detach it from the tab
1340 WebView webView = mMainView;
1341 setWebView(null);
1342 webView.destroy();
1343 }
1344 }
1345
1346 /**
1347 * Remove the tab from the parent
1348 */
1349 void removeFromTree() {
1350 // detach the children
1351 if (mChildTabs != null) {
1352 for(Tab t : mChildTabs) {
1353 t.setParentTab(null);
1354 }
1355 }
1356 // remove itself from the parent list
1357 if (mParentTab != null) {
1358 mParentTab.mChildTabs.remove(this);
1359 }
1360 }
1361
1362 /**
1363 * Create a new subwindow unless a subwindow already exists.
1364 * @return True if a new subwindow was created. False if one already exists.
1365 */
1366 boolean createSubWindow() {
1367 if (mSubView == null) {
Michael Kolb1514bb72010-11-22 09:11:48 -08001368 mWebViewController.createSubWindow(this);
Leon Scroggins III211ba542010-04-19 13:21:13 -04001369 mSubView.setWebViewClient(new SubWindowClient(mWebViewClient,
Michael Kolb8233fac2010-10-26 16:08:53 -07001370 mWebViewController));
Grace Kloba22ac16e2009-10-07 18:00:23 -07001371 mSubView.setWebChromeClient(new SubWindowChromeClient(
1372 mWebChromeClient));
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001373 // Set a different DownloadListener for the mSubView, since it will
1374 // just need to dismiss the mSubView, rather than close the Tab
1375 mSubView.setDownloadListener(new DownloadListener() {
1376 public void onDownloadStart(String url, String userAgent,
1377 String contentDisposition, String mimetype,
1378 long contentLength) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001379 mWebViewController.onDownloadStart(Tab.this, url, userAgent,
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001380 contentDisposition, mimetype, contentLength);
1381 if (mSubView.copyBackForwardList().getSize() == 0) {
1382 // This subwindow was opened for the sole purpose of
1383 // downloading a file. Remove it.
Michael Kolb8233fac2010-10-26 16:08:53 -07001384 mWebViewController.dismissSubWindow(Tab.this);
Leon Scrogginsdcc5eeb2010-02-23 17:26:37 -05001385 }
1386 }
1387 });
Grace Kloba22ac16e2009-10-07 18:00:23 -07001388 mSubView.setOnCreateContextMenuListener(mActivity);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001389 return true;
1390 }
1391 return false;
1392 }
1393
1394 /**
1395 * Dismiss the subWindow for the tab.
1396 */
1397 void dismissSubWindow() {
1398 if (mSubView != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001399 mWebViewController.endActionMode();
Grace Kloba22ac16e2009-10-07 18:00:23 -07001400 BrowserSettings.getInstance().deleteObserver(
1401 mSubView.getSettings());
1402 mSubView.destroy();
1403 mSubView = null;
1404 mSubViewContainer = null;
1405 }
1406 }
1407
Grace Kloba22ac16e2009-10-07 18:00:23 -07001408
1409 /**
1410 * Set the parent tab of this tab.
1411 */
1412 void setParentTab(Tab parent) {
1413 mParentTab = parent;
1414 // This tab may have been freed due to low memory. If that is the case,
1415 // the parent tab index is already saved. If we are changing that index
1416 // (most likely due to removing the parent tab) we must update the
1417 // parent tab index in the saved Bundle.
1418 if (mSavedState != null) {
1419 if (parent == null) {
1420 mSavedState.remove(PARENTTAB);
1421 } else {
Michael Kolb8233fac2010-10-26 16:08:53 -07001422 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl()
Grace Kloba22ac16e2009-10-07 18:00:23 -07001423 .getTabIndex(parent));
1424 }
1425 }
1426 }
1427
1428 /**
1429 * When a Tab is created through the content of another Tab, then we
1430 * associate the Tabs.
1431 * @param child the Tab that was created from this Tab
1432 */
1433 void addChildTab(Tab child) {
1434 if (mChildTabs == null) {
1435 mChildTabs = new Vector<Tab>();
1436 }
1437 mChildTabs.add(child);
1438 child.setParentTab(this);
1439 }
1440
1441 Vector<Tab> getChildTabs() {
1442 return mChildTabs;
1443 }
1444
1445 void resume() {
1446 if (mMainView != null) {
1447 mMainView.onResume();
1448 if (mSubView != null) {
1449 mSubView.onResume();
1450 }
1451 }
1452 }
1453
1454 void pause() {
1455 if (mMainView != null) {
1456 mMainView.onPause();
1457 if (mSubView != null) {
1458 mSubView.onPause();
1459 }
1460 }
1461 }
1462
1463 void putInForeground() {
1464 mInForeground = true;
1465 resume();
1466 mMainView.setOnCreateContextMenuListener(mActivity);
1467 if (mSubView != null) {
1468 mSubView.setOnCreateContextMenuListener(mActivity);
1469 }
1470 // Show the pending error dialog if the queue is not empty
1471 if (mQueuedErrors != null && mQueuedErrors.size() > 0) {
1472 showError(mQueuedErrors.getFirst());
1473 }
Leon Scroggins1961ed22010-12-07 15:22:21 -05001474 mWebViewController.bookmarkedStatusHasChanged(this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001475 }
1476
1477 void putInBackground() {
1478 mInForeground = false;
1479 pause();
1480 mMainView.setOnCreateContextMenuListener(null);
1481 if (mSubView != null) {
1482 mSubView.setOnCreateContextMenuListener(null);
1483 }
1484 }
1485
Michael Kolb8233fac2010-10-26 16:08:53 -07001486 boolean inForeground() {
1487 return mInForeground;
1488 }
1489
Grace Kloba22ac16e2009-10-07 18:00:23 -07001490 /**
1491 * Return the top window of this tab; either the subwindow if it is not
1492 * null or the main window.
1493 * @return The top window of this tab.
1494 */
1495 WebView getTopWindow() {
1496 if (mSubView != null) {
1497 return mSubView;
1498 }
1499 return mMainView;
1500 }
1501
1502 /**
1503 * Return the main window of this tab. Note: if a tab is freed in the
1504 * background, this can return null. It is only guaranteed to be
1505 * non-null for the current tab.
1506 * @return The main WebView of this tab.
1507 */
1508 WebView getWebView() {
1509 return mMainView;
1510 }
1511
Michael Kolba713ec82010-11-29 17:27:06 -08001512 void setViewContainer(View container) {
1513 mContainer = container;
1514 }
1515
Michael Kolb8233fac2010-10-26 16:08:53 -07001516 View getViewContainer() {
1517 return mContainer;
1518 }
1519
Grace Kloba22ac16e2009-10-07 18:00:23 -07001520 /**
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001521 * Return whether private browsing is enabled for the main window of
1522 * this tab.
1523 * @return True if private browsing is enabled.
1524 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001525 boolean isPrivateBrowsingEnabled() {
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001526 WebView webView = getWebView();
1527 if (webView == null) {
1528 return false;
1529 }
1530 return webView.isPrivateBrowsingEnabled();
1531 }
1532
1533 /**
Grace Kloba22ac16e2009-10-07 18:00:23 -07001534 * Return the subwindow of this tab or null if there is no subwindow.
1535 * @return The subwindow of this tab or null.
1536 */
1537 WebView getSubWebView() {
1538 return mSubView;
1539 }
1540
Michael Kolb1514bb72010-11-22 09:11:48 -08001541 void setSubWebView(WebView subView) {
1542 mSubView = subView;
1543 }
1544
Michael Kolb8233fac2010-10-26 16:08:53 -07001545 View getSubViewContainer() {
1546 return mSubViewContainer;
1547 }
1548
Michael Kolb1514bb72010-11-22 09:11:48 -08001549 void setSubViewContainer(View subViewContainer) {
1550 mSubViewContainer = subViewContainer;
1551 }
1552
Grace Kloba22ac16e2009-10-07 18:00:23 -07001553 /**
1554 * @return The geolocation permissions prompt for this tab.
1555 */
1556 GeolocationPermissionsPrompt getGeolocationPermissionsPrompt() {
Grace Kloba50c241e2010-04-20 11:07:50 -07001557 if (mGeolocationPermissionsPrompt == null) {
1558 ViewStub stub = (ViewStub) mContainer
1559 .findViewById(R.id.geolocation_permissions_prompt);
1560 mGeolocationPermissionsPrompt = (GeolocationPermissionsPrompt) stub
1561 .inflate();
1562 mGeolocationPermissionsPrompt.init();
1563 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001564 return mGeolocationPermissionsPrompt;
1565 }
1566
1567 /**
1568 * @return The application id string
1569 */
1570 String getAppId() {
1571 return mAppId;
1572 }
1573
1574 /**
1575 * Set the application id string
1576 * @param id
1577 */
1578 void setAppId(String id) {
1579 mAppId = id;
1580 }
1581
Grace Kloba22ac16e2009-10-07 18:00:23 -07001582 String getUrl() {
John Reck324d4402011-01-11 16:56:42 -08001583 return UrlUtils.filteredUrl(mCurrentState.mUrl);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001584 }
1585
John Reck49a603c2011-03-03 09:33:05 -08001586 String getOriginalUrl() {
1587 if (mMainView == null) {
1588 return "";
1589 }
1590 return UrlUtils.filteredUrl(mMainView.getOriginalUrl());
1591 }
1592
Grace Kloba22ac16e2009-10-07 18:00:23 -07001593 /**
John Reck30c714c2010-12-16 17:30:34 -08001594 * Get the title of this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001595 */
1596 String getTitle() {
John Reck30c714c2010-12-16 17:30:34 -08001597 if (mCurrentState.mTitle == null && mInPageLoad) {
1598 return mActivity.getString(R.string.title_bar_loading);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001599 }
John Reck30c714c2010-12-16 17:30:34 -08001600 return mCurrentState.mTitle;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001601 }
1602
1603 /**
John Reck30c714c2010-12-16 17:30:34 -08001604 * Get the favicon of this tab.
Grace Kloba22ac16e2009-10-07 18:00:23 -07001605 */
1606 Bitmap getFavicon() {
John Reck30c714c2010-12-16 17:30:34 -08001607 return mCurrentState.mFavicon;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001608 }
1609
John Recke969cc52010-12-21 17:24:43 -08001610 public boolean isBookmarkedSite() {
1611 return mCurrentState.mIsBookmarkedSite;
1612 }
Rob Tsukf8bdfce2010-10-07 15:41:16 -07001613
Grace Kloba22ac16e2009-10-07 18:00:23 -07001614 /**
1615 * Return the tab's error console. Creates the console if createIfNEcessary
1616 * is true and we haven't already created the console.
1617 * @param createIfNecessary Flag to indicate if the console should be
1618 * created if it has not been already.
1619 * @return The tab's error console, or null if one has not been created and
1620 * createIfNecessary is false.
1621 */
1622 ErrorConsoleView getErrorConsole(boolean createIfNecessary) {
1623 if (createIfNecessary && mErrorConsole == null) {
1624 mErrorConsole = new ErrorConsoleView(mActivity);
1625 mErrorConsole.setWebView(mMainView);
1626 }
1627 return mErrorConsole;
1628 }
1629
1630 /**
1631 * If this Tab was created through another Tab, then this method returns
1632 * that Tab.
1633 * @return the Tab parent or null
1634 */
1635 public Tab getParentTab() {
1636 return mParentTab;
1637 }
1638
1639 /**
1640 * Return whether this tab should be closed when it is backing out of the
1641 * first page.
1642 * @return TRUE if this tab should be closed when exit.
1643 */
1644 boolean closeOnExit() {
1645 return mCloseOnExit;
1646 }
1647
John Reck30c714c2010-12-16 17:30:34 -08001648 private void setLockIconType(LockIcon icon) {
1649 mCurrentState.mLockIcon = icon;
1650 mWebViewController.onUpdatedLockIcon(this);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001651 }
1652
1653 /**
1654 * @return The tab's lock icon type.
1655 */
John Reck30c714c2010-12-16 17:30:34 -08001656 LockIcon getLockIconType() {
1657 return mCurrentState.mLockIcon;
1658 }
1659
1660 int getLoadProgress() {
1661 if (mInPageLoad) {
1662 return mPageLoadProgress;
1663 }
1664 return 100;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001665 }
1666
1667 /**
1668 * @return TRUE if onPageStarted is called while onPageFinished is not
1669 * called yet.
1670 */
Michael Kolb8233fac2010-10-26 16:08:53 -07001671 boolean inPageLoad() {
1672 return mInPageLoad;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001673 }
1674
1675 // force mInLoad to be false. This should only be called before closing the
1676 // tab to ensure BrowserActivity's pauseWebViewTimers() is called correctly.
Michael Kolb8233fac2010-10-26 16:08:53 -07001677 void clearInPageLoad() {
1678 mInPageLoad = false;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001679 }
1680
Grace Kloba22ac16e2009-10-07 18:00:23 -07001681 /**
John Reck30c714c2010-12-16 17:30:34 -08001682 * Get the cached saved state bundle.
1683 * @return cached state bundle
Grace Kloba22ac16e2009-10-07 18:00:23 -07001684 */
1685 Bundle getSavedState() {
1686 return mSavedState;
1687 }
1688
1689 /**
1690 * Set the saved state.
1691 */
1692 void setSavedState(Bundle state) {
1693 mSavedState = state;
1694 }
1695
1696 /**
1697 * @return TRUE if succeed in saving the state.
1698 */
1699 boolean saveState() {
1700 // If the WebView is null it means we ran low on memory and we already
1701 // stored the saved state in mSavedState.
1702 if (mMainView == null) {
1703 return mSavedState != null;
1704 }
1705
1706 mSavedState = new Bundle();
1707 final WebBackForwardList list = mMainView.saveState(mSavedState);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001708
1709 // Store some extra info for displaying the tab in the picker.
1710 final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001711
John Reck30c714c2010-12-16 17:30:34 -08001712 mSavedState.putString(CURRURL, mCurrentState.mUrl);
1713 mSavedState.putString(CURRTITLE, mCurrentState.mTitle);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001714 mSavedState.putBoolean(CLOSEONEXIT, mCloseOnExit);
1715 if (mAppId != null) {
1716 mSavedState.putString(APPID, mAppId);
1717 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001718 // Remember the parent tab so the relationship can be restored.
1719 if (mParentTab != null) {
Michael Kolb8233fac2010-10-26 16:08:53 -07001720 mSavedState.putInt(PARENTTAB, mWebViewController.getTabControl().getTabIndex(
Grace Kloba22ac16e2009-10-07 18:00:23 -07001721 mParentTab));
1722 }
1723 return true;
1724 }
1725
1726 /*
1727 * Restore the state of the tab.
1728 */
1729 boolean restoreState(Bundle b) {
1730 if (b == null) {
1731 return false;
1732 }
1733 // Restore the internal state even if the WebView fails to restore.
1734 // This will maintain the app id, original url and close-on-exit values.
1735 mSavedState = null;
Grace Kloba22ac16e2009-10-07 18:00:23 -07001736 mCloseOnExit = b.getBoolean(CLOSEONEXIT);
1737 mAppId = b.getString(APPID);
Grace Kloba22ac16e2009-10-07 18:00:23 -07001738
1739 final WebBackForwardList list = mMainView.restoreState(b);
1740 if (list == null) {
1741 return false;
1742 }
Grace Kloba22ac16e2009-10-07 18:00:23 -07001743 return true;
1744 }
Leon Scroggins III211ba542010-04-19 13:21:13 -04001745
Leon Scroggins1961ed22010-12-07 15:22:21 -05001746 public void updateBookmarkedStatus() {
John Recke969cc52010-12-21 17:24:43 -08001747 mDataController.queryBookmarkStatus(getUrl(), mIsBookmarkCallback);
Leon Scroggins1961ed22010-12-07 15:22:21 -05001748 }
1749
John Recke969cc52010-12-21 17:24:43 -08001750 private DataController.OnQueryUrlIsBookmark mIsBookmarkCallback
1751 = new DataController.OnQueryUrlIsBookmark() {
1752 @Override
1753 public void onQueryUrlIsBookmark(String url, boolean isBookmark) {
1754 if (mCurrentState.mUrl.equals(url)) {
1755 mCurrentState.mIsBookmarkedSite = isBookmark;
1756 mWebViewController.bookmarkedStatusHasChanged(Tab.this);
1757 }
Leon Scroggins1961ed22010-12-07 15:22:21 -05001758 }
John Recke969cc52010-12-21 17:24:43 -08001759 };
Michael Kolb1acef692011-03-08 14:12:06 -08001760
Grace Kloba22ac16e2009-10-07 18:00:23 -07001761}