package com.android.browser;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Paint;
import android.graphics.Picture;
+import android.graphics.Shader;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Message;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageButton;
+import android.widget.LinearLayout;
import java.io.File;
import java.io.FileInputStream;
private final LayoutInflater mInflateService;
// Subclass of WebViewClient used in subwindows to notify the main
// WebViewClient of certain WebView activities.
- private class SubWindowClient extends WebViewClient {
+ private static class SubWindowClient extends WebViewClient {
// The main WebViewClient.
private final WebViewClient mClient;
String description, String failingUrl) {
mClient.onReceivedError(view, errorCode, description, failingUrl);
}
+ @Override
+ public boolean shouldOverrideKeyEvent(WebView view,
+ android.view.KeyEvent event) {
+ return mClient.shouldOverrideKeyEvent(view, event);
+ }
+ @Override
+ public void onUnhandledKeyEvent(WebView view,
+ android.view.KeyEvent event) {
+ mClient.onUnhandledKeyEvent(view, event);
+ }
}
// Subclass of WebChromeClient to display javascript dialogs.
private class SubWindowChromeClient extends WebChromeClient {
public void onRequestFocus(WebView view) {
Tab t = getTabFromView(view);
if (t != getCurrentTab()) {
- mActivity.showTab(t);
+ mActivity.switchToTab(getTabIndex(t));
}
}
}
public static class PickerData {
String mUrl;
String mTitle;
+ Bitmap mFavicon;
float mScale;
int mScrollX;
int mScrollY;
- int mWidth;
- Picture mPicture;
- // This can be null. When a new picture comes in, this view should be
- // invalidated to show the new picture.
- FakeWebView mFakeWebView;
}
/**
* Private class for maintaining Tabs with a main WebView and a subwindow.
*/
- public class Tab implements WebView.PictureListener {
+ public class Tab {
+ // The Geolocation permissions prompt
+ private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
+ private View mContainer;
// Main WebView
private WebView mMainView;
// Subwindow WebView
// url has not changed.
private String mOriginalUrl;
+ private ErrorConsoleView mErrorConsole;
+ // the lock icon type and previous lock icon type for the tab
+ private int mSavedLockIconType;
+ private int mSavedPrevLockIconType;
+
// Construct a new tab
- private Tab(WebView w, boolean closeOnExit, String appId, String url) {
- mMainView = w;
+ private Tab(WebView w, boolean closeOnExit, String appId, String url, Context context) {
mCloseOnExit = closeOnExit;
mAppId = appId;
mOriginalUrl = url;
+ mSavedLockIconType = BrowserActivity.LOCK_ICON_UNSECURE;
+ mSavedPrevLockIconType = BrowserActivity.LOCK_ICON_UNSECURE;
+
+ // The tab consists of a container view, which contains the main
+ // WebView, as well as any other UI elements associated with the tab.
+ LayoutInflater factory = LayoutInflater.from(context);
+ mContainer = factory.inflate(R.layout.tab, null);
+
+ mGeolocationPermissionsPrompt =
+ (GeolocationPermissionsPrompt) mContainer.findViewById(
+ R.id.geolocation_permissions_prompt);
+
+ setWebView(w);
+ }
+
+ /**
+ * Sets the WebView for this tab, correctly removing the old WebView
+ * from the container view.
+ */
+ public void setWebView(WebView w) {
+ if (mMainView == w) {
+ return;
+ }
+ // If the WebView is changing, the page will be reloaded, so any ongoing Geolocation
+ // permission requests are void.
+ mGeolocationPermissionsPrompt.hide();
+
+ // Just remove the old one.
+ FrameLayout wrapper =
+ (FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
+ wrapper.removeView(mMainView);
+ mMainView = w;
+ }
+
+ /**
+ * This method attaches both the WebView and any sub window to the
+ * given content view.
+ */
+ public void attachTabToContentView(ViewGroup content) {
+ if (mMainView == null) {
+ return;
+ }
+
+ // Attach the WebView to the container and then attach the
+ // container to the content view.
+ FrameLayout wrapper =
+ (FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
+ wrapper.addView(mMainView);
+ content.addView(mContainer, BrowserActivity.COVER_SCREEN_PARAMS);
+ attachSubWindow(content);
+ }
+
+ /**
+ * Remove the WebView and any sub window from the given content view.
+ */
+ public void removeTabFromContentView(ViewGroup content) {
+ if (mMainView == null) {
+ return;
+ }
+
+ // Remove the container from the content and then remove the
+ // WebView from the container. This will trigger a focus change
+ // needed by WebView.
+ FrameLayout wrapper =
+ (FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
+ wrapper.removeView(mMainView);
+ content.removeView(mContainer);
+ removeSubWindow(content);
+ }
+
+ /**
+ * Attach the sub window to the content view.
+ */
+ public void attachSubWindow(ViewGroup content) {
+ if (mSubView != null) {
+ content.addView(mSubViewContainer,
+ BrowserActivity.COVER_SCREEN_PARAMS);
+ }
+ }
+
+ /**
+ * Remove the sub window from the content view.
+ */
+ public void removeSubWindow(ViewGroup content) {
+ if (mSubView != null) {
+ content.removeView(mSubViewContainer);
+ }
}
/**
}
/**
- * Return the subwindow of this tab or null if there is no subwindow.
- * @return The subwindow of this tab or null.
+ * @return The geolocation permissions prompt for this tab.
*/
- public WebView getSubWebView() {
- return mSubView;
+ public GeolocationPermissionsPrompt getGeolocationPermissionsPrompt() {
+ return mGeolocationPermissionsPrompt;
}
/**
- * Return the subwindow container of this tab or null if there is no
- * subwindow.
- * @return The subwindow's container View.
+ * Return the subwindow of this tab or null if there is no subwindow.
+ * @return The subwindow of this tab or null.
*/
- public View getSubWebViewContainer() {
- return mSubViewContainer;
+ public WebView getSubWebView() {
+ return mSubView;
}
/**
return null;
}
- /**
- * Returns the picker data.
- */
- public PickerData getPickerData() {
- return mPickerData;
+ public Bitmap getFavicon() {
+ if (mPickerData != null) {
+ return mPickerData.mFavicon;
+ }
+ return null;
}
private void setParentTab(Tab parent) {
return mCloseOnExit;
}
- public void onNewPicture(WebView view, Picture p) {
- if (mPickerData == null) {
- return;
- }
+ void setLockIconType(int type) {
+ mSavedLockIconType = type;
+ }
- mPickerData.mPicture = p;
- // Tell the FakeWebView to redraw.
- if (mPickerData.mFakeWebView != null) {
- mPickerData.mFakeWebView.invalidate();
- }
+ int getLockIconType() {
+ return mSavedLockIconType;
+ }
+
+ void setPrevLockIconType(int type) {
+ mSavedPrevLockIconType = type;
+ }
+
+ int getPrevLockIconType() {
+ return mSavedPrevLockIconType;
}
};
}
/**
+ * Return the current tab's error console. Creates the console if createIfNEcessary
+ * is true and we haven't already created the console.
+ * @param createIfNecessary Flag to indicate if the console should be created if it has
+ * not been already.
+ * @return The current tab's error console, or null if one has not been created and
+ * createIfNecessary is false.
+ */
+ ErrorConsoleView getCurrentErrorConsole(boolean createIfNecessary) {
+ Tab t = getTab(mCurrentTab);
+ if (t == null) {
+ return null;
+ }
+
+ if (createIfNecessary && t.mErrorConsole == null) {
+ t.mErrorConsole = new ErrorConsoleView(mActivity);
+ t.mErrorConsole.setWebView(t.mMainView);
+ }
+
+ return t.mErrorConsole;
+ }
+
+ /**
* Return the current tab's top-level WebView. This can return a subwindow
* if one exists.
* @return The top-level WebView of the current tab.
return null;
}
final WebView w = createNewWebView();
+
// Create a new tab and add it to the tab list
- Tab t = new Tab(w, closeOnExit, appId, url);
+ Tab t = new Tab(w, closeOnExit, appId, url, mActivity);
mTabs.add(t);
// Initially put the tab in the background.
putTabInBackground(t);
// observers.
BrowserSettings.getInstance().deleteObserver(
t.mMainView.getSettings());
- // Destroy the main view and subview
- t.mMainView.destroy();
- t.mMainView = null;
+ WebView w = t.mMainView;
+ t.setWebView(null);
+ // Destroy the main view
+ w.destroy();
}
// clear it's references to parent and children
t.removeFromTree();
if (t.mMainView != null) {
dismissSubWindow(t);
s.deleteObserver(t.mMainView.getSettings());
- t.mMainView.destroy();
- t.mMainView = null;
+ WebView w = t.mMainView;
+ t.setWebView(null);
+ w.destroy();
}
}
mTabs.clear();
private static final String CURRTAB = "currentTab";
private static final String CURRURL = "currentUrl";
private static final String CURRTITLE = "currentTitle";
- private static final String CURRWIDTH = "currentWidth";
private static final String CURRPICTURE = "currentPicture";
private static final String CLOSEONEXIT = "closeonexit";
private static final String PARENTTAB = "parentTab";
} else {
// Create a new tab and don't restore the state yet, add it
// to the tab list
- Tab t = new Tab(null, false, null, null);
+ Tab t = new Tab(null, false, null, null, mActivity);
t.mSavedState = inState.getBundle(WEBVIEW + i);
if (t.mSavedState != null) {
populatePickerDataFromSavedState(t);
* WebView cache;
*/
void freeMemory() {
+ if (getTabCount() == 0) return;
+
// free the least frequently used background tab
- Tab t = getLeastUsedTab();
+ Tab t = getLeastUsedTab(getCurrentTab());
if (t != null) {
Log.w(LOGTAG, "Free a tab in the browser");
freeTab(t);
System.gc();
}
- private Tab getLeastUsedTab() {
- // Don't do anything if we only have 1 tab.
- if (getTabCount() == 1) {
+ private Tab getLeastUsedTab(Tab current) {
+ // Don't do anything if we only have 1 tab or if the current tab is
+ // null.
+ if (getTabCount() == 1 || current == null) {
return null;
}
}
do {
t = mTabQueue.get(i++);
- } while (i < queueSize && t != null && t.mMainView == null);
+ } while (i < queueSize
+ && ((t != null && t.mMainView == null)
+ || t == current.mParentTab));
// Don't do anything if the last remaining tab is the current one or if
// the last tab has been freed already.
- if (t == getCurrentTab() || t.mMainView == null) {
+ if (t == current || t.mMainView == null) {
return null;
}
// Remove the WebView's settings from the BrowserSettings list of
// observers.
BrowserSettings.getInstance().deleteObserver(t.mMainView.getSettings());
- t.mMainView.destroy();
- t.mMainView = null;
+ WebView w = t.mMainView;
+ t.setWebView(null);
+ w.destroy();
}
/**
}
// Create a new WebView. If this tab is the current tab, we need to put
// back all the clients so force it to be the current tab.
- t.mMainView = createNewWebView();
+ t.setWebView(createNewWebView());
if (getCurrentTab() == t) {
setCurrentTab(t, true);
}
private WebView createNewWebView() {
// Create a new WebView
WebView w = new WebView(mActivity);
+ w.setScrollbarFadingEnabled(true);
+ w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
w.setMapTrackballToArrowKeys(false); // use trackball directly
// Enable the built-in zoom
w.getSettings().setBuiltInZoomControls(true);
// settings
final BrowserSettings s = BrowserSettings.getInstance();
s.addObserver(w.getSettings()).update(s, null);
+
+ // pick a default
+ if (false) {
+ MeshTracker mt = new MeshTracker(2);
+ Paint paint = new Paint();
+ Bitmap bm = BitmapFactory.decodeResource(mActivity.getResources(),
+ R.drawable.pattern_carbon_fiber_dark);
+ paint.setShader(new BitmapShader(bm, Shader.TileMode.REPEAT,
+ Shader.TileMode.REPEAT));
+ mt.setBGPaint(paint);
+ w.setDragTracker(mt);
+ }
return w;
}
boolean needRestore = (mainView == null);
if (needRestore) {
// Same work as in createNewTab() except don't do new Tab()
- newTab.mMainView = mainView = createNewWebView();
+ mainView = createNewWebView();
+ newTab.setWebView(mainView);
}
putViewInForeground(mainView, mActivity.getWebViewClient(),
mActivity.getWebChromeClient());
final WebHistoryItem item =
list != null ? list.getCurrentItem() : null;
populatePickerData(t, item);
-
- // This method is only called during the tab picker creation. At this
- // point we need to listen for new pictures since the WebView is still
- // active.
- final WebView w = t.getTopWindow();
- w.setPictureListener(t);
- // Capture the picture here instead of populatePickerData since it can
- // be called when saving the state of a tab.
- t.mPickerData.mPicture = w.capturePicture();
}
// Create the PickerData and populate it using the saved state of the tab.
final Bundle state = t.mSavedState;
data.mUrl = state.getString(CURRURL);
data.mTitle = state.getString(CURRTITLE);
- data.mWidth = state.getInt(CURRWIDTH, 0);
// XXX: These keys are from WebView.savePicture so if they change, this
// will break.
data.mScale = state.getFloat("scale", 1.0f);
data.mScrollX = state.getInt("scrollX", 0);
data.mScrollY = state.getInt("scrollY", 0);
- if (state.containsKey(CURRPICTURE)) {
- final File f = new File(t.mSavedState.getString(CURRPICTURE));
- try {
- final FileInputStream in = new FileInputStream(f);
- data.mPicture = Picture.createFromStream(in);
- in.close();
- } catch (Exception ex) {
- // Ignore any problems with inflating the picture. We just
- // won't draw anything.
- }
- }
-
// Set the tab's picker data.
t.mPickerData = data;
}
if (item != null) {
data.mUrl = item.getUrl();
data.mTitle = item.getTitle();
+ data.mFavicon = item.getFavicon();
if (data.mTitle == null) {
data.mTitle = data.mUrl;
}
// We want to display the top window in the tab picker but use the url
// and title of the main window.
final WebView w = t.getTopWindow();
- data.mWidth = w.getWidth();
data.mScale = w.getScale();
data.mScrollX = w.getScrollX();
data.mScrollY = w.getScrollY();
- // Remember the old picture if possible.
- if (t.mPickerData != null) {
- data.mPicture = t.mPickerData.mPicture;
- }
t.mPickerData = data;
}
if (t != null && t.mSavedState == null) {
t.mPickerData = null;
}
- if (t.mMainView != null) {
- // Clear the picture listeners.
- t.mMainView.setPictureListener(null);
- if (t.mSubView != null) {
- t.mSubView.setPictureListener(null);
- }
- }
}
}
if (data.mTitle != null) {
b.putString(CURRTITLE, data.mTitle);
}
- b.putInt(CURRWIDTH, data.mWidth);
b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
if (t.mAppId != null) {
b.putString(APPID, t.mAppId);