diff options
author | Ben Murdoch <benm@google.com> | 2009-07-01 20:19:05 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2009-07-17 11:22:26 +0100 |
commit | bff2d603c022691237c31d9a57ad8c217c6e7e11 (patch) | |
tree | 3e29889b7e51881715cb3fbb32b197c81d877754 /src | |
parent | cd11589fc3930906d4b9b7dd18aa52a9f1eb0c8a (diff) | |
download | packages_apps_Browser-bff2d603c022691237c31d9a57ad8c217c6e7e11.zip packages_apps_Browser-bff2d603c022691237c31d9a57ad8c217c6e7e11.tar.gz packages_apps_Browser-bff2d603c022691237c31d9a57ad8c217c6e7e11.tar.bz2 |
Implement an error console. The console is displayed when the user has enabled debug in the browser (been to about:debug) and there are errors on the page. It can be toggled on/off in debug mode in the settings menu.
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/browser/BrowserActivity.java | 87 | ||||
-rw-r--r-- | src/com/android/browser/BrowserSettings.java | 17 | ||||
-rw-r--r-- | src/com/android/browser/ErrorConsoleView.java | 339 | ||||
-rw-r--r-- | src/com/android/browser/TabControl.java | 24 |
4 files changed, 460 insertions, 7 deletions
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index 1bccd41..da950ed 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -689,6 +689,8 @@ public class BrowserActivity extends Activity mTitleBar.setBrowserActivity(this); mContentView = (FrameLayout) browserFrameLayout.findViewById( R.id.main_content); + mErrorConsoleContainer = (LinearLayout) browserFrameLayout.findViewById( + R.id.error_console); mCustomViewContainer = (FrameLayout) browserFrameLayout .findViewById(R.id.fullscreen_custom_content); frameLayout.addView(browserFrameLayout, COVER_SCREEN_PARAMS); @@ -696,8 +698,15 @@ public class BrowserActivity extends Activity mCustomViewContainer = new FrameLayout(this); mCustomViewContainer.setBackgroundColor(Color.BLACK); mContentView = new FrameLayout(this); + + LinearLayout linearLayout = new LinearLayout(this); + linearLayout.setOrientation(LinearLayout.VERTICAL); + mErrorConsoleContainer = new LinearLayout(this); + linearLayout.addView(mErrorConsoleContainer, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + linearLayout.addView(mContentView, COVER_SCREEN_PARAMS); frameLayout.addView(mCustomViewContainer, COVER_SCREEN_PARAMS); - frameLayout.addView(mContentView, COVER_SCREEN_PARAMS); + frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS); } // Create the tab control and our initial tab @@ -904,7 +913,7 @@ public class BrowserActivity extends Activity // page, it can be reused. boolean needsLoad = mTabControl.recreateWebView(appTab, urlData.mUrl); - + if (current != appTab) { showTab(appTab, needsLoad ? urlData : EMPTY_URL_DATA); } else { @@ -1979,6 +1988,20 @@ public class BrowserActivity extends Activity final WebView main = t.getWebView(); // Attach the main WebView. mContentView.addView(main, COVER_SCREEN_PARAMS); + + if (mShouldShowErrorConsole) { + ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true); + if (errorConsole.numberOfErrors() == 0) { + errorConsole.showConsole(ErrorConsoleView.SHOW_NONE); + } else { + errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED); + } + + mErrorConsoleContainer.addView(errorConsole, + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + } + // Attach the sub window if necessary attachSubWindow(t); // Request focus on the top window. @@ -2000,6 +2023,11 @@ public class BrowserActivity extends Activity private void removeTabFromContentView(TabControl.Tab t) { // Remove the main WebView. mContentView.removeView(t.getWebView()); + + if (mTabControl.getCurrentErrorConsole(false) != null) { + mErrorConsoleContainer.removeView(mTabControl.getCurrentErrorConsole(false)); + } + // Remove the sub window if it exists. if (t.getSubWebView() != null) { mContentView.removeView(t.getSubWebViewContainer()); @@ -2977,6 +3005,15 @@ public class BrowserActivity extends Activity public void onPageStarted(WebView view, String url, Bitmap favicon) { resetLockIcon(url); setUrlTitle(url, null); + + ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(false); + if (errorConsole != null) { + errorConsole.clearErrorMessages(); + if (mShouldShowErrorConsole) { + errorConsole.showConsole(ErrorConsoleView.SHOW_NONE); + } + } + // Call updateIcon instead of setFavicon so the bookmark // database can be updated. updateIcon(url, favicon); @@ -3197,9 +3234,9 @@ public class BrowserActivity extends Activity if (url.startsWith("about:")) { return false; } - + Intent intent; - + // perform generic parsing of the URI to turn it into an Intent. try { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); @@ -3852,9 +3889,14 @@ public class BrowserActivity extends Activity */ @Override public void addMessageToConsole(String message, int lineNumber, String sourceID) { - Log.w(LOGTAG, "Console: " + message + " (" + sourceID + ":" + lineNumber + ")"); + ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true); + errorConsole.addErrorMessage(message, sourceID, lineNumber); + if (mShouldShowErrorConsole && + errorConsole.getShowState() != ErrorConsoleView.SHOW_MAXIMIZED) { + errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED); + } + Log.w(LOGTAG, "Console: " + message + " " + sourceID + ":" + lineNumber); } - }; /** @@ -4922,6 +4964,34 @@ public class BrowserActivity extends Activity return URLUtil.composeSearchUrl(inUrl, QuickSearch_G, QUERY_PLACE_HOLDER); } + /* package */ void setShouldShowErrorConsole(boolean flag) { + if (flag == mShouldShowErrorConsole) { + // Nothing to do. + return; + } + + mShouldShowErrorConsole = flag; + + ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true); + + if (flag) { + // Setting the show state of the console will cause it's the layout to be inflated. + if (errorConsole.numberOfErrors() > 0) { + errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED); + } else { + errorConsole.showConsole(ErrorConsoleView.SHOW_NONE); + } + + // Now we can add it to the main view. + mErrorConsoleContainer.addView(errorConsole, + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + } else { + mErrorConsoleContainer.removeView(errorConsole); + } + + } + private final static int LOCK_ICON_UNSECURE = 0; private final static int LOCK_ICON_SECURE = 1; private final static int LOCK_ICON_MIXED = 2; @@ -5073,6 +5143,9 @@ public class BrowserActivity extends Activity private TitleBar mTitleBar; + private LinearLayout mErrorConsoleContainer = null; + private boolean mShouldShowErrorConsole = false; + // Used during animations to prevent other animations from being triggered. // A count is used since the animation to and from the Window overview can // overlap. A count of 0 means no animation where a count of > 0 means @@ -5146,7 +5219,7 @@ public class BrowserActivity extends Activity String mEncoding; @Override boolean isEmpty() { - return mInlined == null || mInlined.length() == 0 || super.isEmpty(); + return mInlined == null || mInlined.length() == 0 || super.isEmpty(); } @Override diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index aa7e103..4d10fe2 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -91,6 +91,10 @@ class BrowserSettings extends Observable { private boolean lightTouch = false; private boolean navDump = false; + // By default the error console is shown once the user navigates to about:debug. + // The setting can be then toggled from the settings menu. + private boolean showConsole = true; + // Browser only settings private boolean doFlick = false; @@ -205,6 +209,10 @@ class BrowserSettings extends Observable { // Turn on Application Caches. s.setAppCachePath(b.appCachePath); s.setAppCacheEnabled(b.appCacheEnabled); + + // Enable/Disable the error console. + b.mTabControl.getBrowserActivity().setShouldShowErrorConsole( + b.showDebugSettings && b.showConsole); } } @@ -325,6 +333,15 @@ class BrowserSettings extends Observable { // JS flags is loaded from DB even if showDebugSettings is false, // so that it can be set once and be effective all the time. jsFlags = p.getString("js_engine_flags", ""); + + // Read the setting for showing/hiding the JS Console always so that should the + // user enable debug settings, we already know if we should show the console. + // The user will never see the console unless they navigate to about:debug, + // regardless of the setting we read here. This setting is only used after debug + // is enabled. + showConsole = p.getBoolean("javascript_console", showConsole); + mTabControl.getBrowserActivity().setShouldShowErrorConsole( + showDebugSettings && showConsole); } public String getPluginsPath() { diff --git a/src/com/android/browser/ErrorConsoleView.java b/src/com/android/browser/ErrorConsoleView.java new file mode 100644 index 0000000..56f663b --- /dev/null +++ b/src/com/android/browser/ErrorConsoleView.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.browser; + +import android.content.Context; +import android.database.DataSetObserver; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TwoLineListItem; + +import java.util.Vector; + +/* package */ class ErrorConsoleView extends LinearLayout { + + /** + * Define some constants to describe the visibility of the error console. + */ + public static final int SHOW_MINIMIZED = 0; + public static final int SHOW_MAXIMIZED = 1; + public static final int SHOW_NONE = 2; + + private TextView mConsoleHeader; + private ErrorConsoleListView mErrorList; + private LinearLayout mEvalJsViewGroup; + private EditText mEvalEditText; + private Button mEvalButton; + private WebView mWebView; + private int mCurrentShowState = SHOW_NONE; + + private boolean mSetupComplete = false; + + // Before we've been asked to display the console, cache any messages that should + // be added to the console. Then when we do display the console, add them to the view + // then. + private Vector<ErrorConsoleMessage> mErrorMessageCache; + + public ErrorConsoleView(Context context) { + super(context); + } + + public ErrorConsoleView(Context context, AttributeSet attributes) { + super(context, attributes); + } + + private void commonSetupIfNeeded() { + if (mSetupComplete) { + return; + } + + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.error_console, this); + + // Get references to each ui element. + mConsoleHeader = (TextView) findViewById(R.id.error_console_header_id); + mErrorList = (ErrorConsoleListView) findViewById(R.id.error_console_list_id); + mEvalJsViewGroup = (LinearLayout) findViewById(R.id.error_console_eval_view_group_id); + mEvalEditText = (EditText) findViewById(R.id.error_console_eval_text_id); + mEvalButton = (Button) findViewById(R.id.error_console_eval_button_id); + + mEvalButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + // Send the javascript to be evaluated to webkit as a javascript: url + // TODO: Can we expose access to webkit's JS interpreter here and evaluate it that + // way? Note that this is called on the UI thread so we will need to post a message + // to the WebCore thread to implement this. + if (mWebView != null) { + mWebView.loadUrl("javascript:" + mEvalEditText.getText()); + } + + mEvalEditText.setText(""); + } + }); + + // Make clicking on the console title bar min/maximse it. + mConsoleHeader.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (mCurrentShowState == SHOW_MINIMIZED) { + showConsole(SHOW_MAXIMIZED); + } else { + showConsole(SHOW_MINIMIZED); + } + } + }); + + // Add any cached messages to the list now that we've assembled the view. + if (mErrorMessageCache != null) { + for (ErrorConsoleMessage msg : mErrorMessageCache) { + mErrorList.addErrorMessage(msg.getMessage(), msg.getSourceID(), msg.getLineNumber()); + } + mErrorMessageCache.clear(); + } + + mSetupComplete = true; + } + + /** + * Adds a message to the set of messages the console uses. + */ + public void addErrorMessage(String msg, String sourceId, int lineNumber) { + if (mSetupComplete) { + mErrorList.addErrorMessage(msg, sourceId, lineNumber); + } else { + if (mErrorMessageCache == null) { + mErrorMessageCache = new Vector<ErrorConsoleMessage>(); + } + mErrorMessageCache.add(new ErrorConsoleMessage(msg, sourceId, lineNumber)); + } + } + + /** + * Removes all error messages from the console. + */ + public void clearErrorMessages() { + if (mSetupComplete) { + mErrorList.clearErrorMessages(); + } else if (mErrorMessageCache != null) { + mErrorMessageCache.clear(); + } + } + + /** + * Returns the current number of errors displayed in the console. + */ + public int numberOfErrors() { + if (mSetupComplete) { + return mErrorList.getCount(); + } else { + return (mErrorMessageCache == null) ? 0 : mErrorMessageCache.size(); + } + } + + /** + * Sets the webview that this console is associated with. Currently this is used so + * we can call into webkit to evaluate JS expressions in the console. + */ + public void setWebView(WebView webview) { + mWebView = webview; + } + + /** + * Sets the visibility state of the console. + */ + public void showConsole(int show_state) { + commonSetupIfNeeded(); + switch (show_state) { + case SHOW_MINIMIZED: + mConsoleHeader.setVisibility(View.VISIBLE); + mConsoleHeader.setText(R.string.error_console_header_text_minimized); + mErrorList.setVisibility(View.GONE); + mEvalJsViewGroup.setVisibility(View.GONE); + break; + + case SHOW_MAXIMIZED: + mConsoleHeader.setVisibility(View.VISIBLE); + mConsoleHeader.setText(R.string.error_console_header_text_maximized); + mErrorList.setVisibility(View.VISIBLE); + mEvalJsViewGroup.setVisibility(View.VISIBLE); + break; + + case SHOW_NONE: + mConsoleHeader.setVisibility(View.GONE); + mErrorList.setVisibility(View.GONE); + mEvalJsViewGroup.setVisibility(View.GONE); + break; + } + mCurrentShowState = show_state; + } + + /** + * Returns the current visibility state of the console. + */ + public int getShowState() { + if (mSetupComplete) { + return mCurrentShowState; + } else { + return SHOW_NONE; + } + } + + /** + * This class extends ListView to implement the View that will actually display the set of + * errors encountered on the current page. + */ + private static class ErrorConsoleListView extends ListView { + // An adapter for this View that contains a list of error messages. + private ErrorConsoleMessageList mConsoleMessages; + + public ErrorConsoleListView(Context context, AttributeSet attributes) { + super(context, attributes); + mConsoleMessages = new ErrorConsoleMessageList(context); + setAdapter(mConsoleMessages); + } + + public void addErrorMessage(String msg, String sourceId, int lineNumber) { + mConsoleMessages.add(msg, sourceId, lineNumber); + setSelection(mConsoleMessages.getCount()); + } + + public void clearErrorMessages() { + mConsoleMessages.clear(); + } + + /** + * This class is an adapter for ErrorConsoleListView that contains the error console + * message data. + */ + private class ErrorConsoleMessageList extends android.widget.BaseAdapter + implements android.widget.ListAdapter { + + private Vector<ErrorConsoleMessage> mMessages; + private LayoutInflater mInflater; + + public ErrorConsoleMessageList(Context context) { + mMessages = new Vector<ErrorConsoleMessage>(); + mInflater = (LayoutInflater)context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + } + + /** + * Add a new message to the list and update the View. + */ + public void add(String msg, String sourceID, int lineNumber) { + mMessages.add(new ErrorConsoleMessage(msg, sourceID, lineNumber)); + notifyDataSetChanged(); + } + + /** + * Remove all messages from the list and update the view. + */ + public void clear() { + mMessages.clear(); + notifyDataSetChanged(); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + public long getItemId(int position) { + return position; + } + + public Object getItem(int position) { + return mMessages.get(position); + } + + public int getCount() { + return mMessages.size(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + /** + * Constructs a TwoLineListItem for the error at position. + */ + public View getView(int position, View convertView, ViewGroup parent) { + View view; + ErrorConsoleMessage error = mMessages.get(position); + + if (error == null) { + return null; + } + + if (convertView == null) { + view = mInflater.inflate(android.R.layout.two_line_list_item, parent, false); + } else { + view = convertView; + } + + TextView headline = (TextView) view.findViewById(android.R.id.text1); + TextView subText = (TextView) view.findViewById(android.R.id.text2); + headline.setText(error.getSourceID() + ":" + error.getLineNumber()); + subText.setText(error.getMessage()); + return view; + } + + } + } + + /** + * This class holds the data for a single error message in the console. + */ + private static class ErrorConsoleMessage { + private String mMessage; + private String mSourceID; + private int mLineNumber; + + public ErrorConsoleMessage(String msg, String sourceID, int lineNumber) { + mMessage = msg; + mSourceID = sourceID; + mLineNumber = lineNumber; + } + + public String getMessage() { + return mMessage; + } + + public String getSourceID() { + return mSourceID; + } + + public int getLineNumber() { + return mLineNumber; + } + } +} diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java index 1c8b7c8..c7c3e3f 100644 --- a/src/com/android/browser/TabControl.java +++ b/src/com/android/browser/TabControl.java @@ -195,6 +195,8 @@ class TabControl { // url has not changed. private String mOriginalUrl; + private ErrorConsoleView mErrorConsole; + // Construct a new tab private Tab(WebView w, boolean closeOnExit, String appId, String url) { mMainView = w; @@ -388,6 +390,28 @@ class TabControl { } /** + * 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. |