diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-10-14 16:28:11 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-10-15 11:18:51 +0100 |
commit | 94c0057d67c2e0a4b88a4f735388639210260d0e (patch) | |
tree | a19b51f784a34c2601739f5c07e6b932195f8762 /core | |
parent | d892afc88d3c67a7fe1c9550bfa7a452051d031d (diff) | |
download | frameworks_base-94c0057d67c2e0a4b88a4f735388639210260d0e.zip frameworks_base-94c0057d67c2e0a4b88a4f735388639210260d0e.tar.gz frameworks_base-94c0057d67c2e0a4b88a4f735388639210260d0e.tar.bz2 |
Delete the old WebView.
Delete all the Java classes used only by the old WebView implementation,
and also sections of common classes that were only needed for the old
WebView.
Bug: 10427705
Change-Id: I02549a71104b35d86d99058c71f43e054730ec7d
Diffstat (limited to 'core')
55 files changed, 0 insertions, 29789 deletions
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java deleted file mode 100644 index abc078b..0000000 --- a/core/java/android/webkit/AccessibilityInjector.java +++ /dev/null @@ -1,976 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.SystemClock; -import android.provider.Settings; -import android.speech.tts.TextToSpeech; -import android.speech.tts.TextToSpeech.Engine; -import android.speech.tts.TextToSpeech.OnInitListener; -import android.speech.tts.UtteranceProgressListener; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.webkit.WebViewCore.EventHub; - -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Handles injecting accessibility JavaScript and related JavaScript -> Java - * APIs. - */ -class AccessibilityInjector { - private static final String TAG = AccessibilityInjector.class.getSimpleName(); - - private static boolean DEBUG = false; - - // The WebViewClassic this injector is responsible for managing. - private final WebViewClassic mWebViewClassic; - - // Cached reference to mWebViewClassic.getContext(), for convenience. - private final Context mContext; - - // Cached reference to mWebViewClassic.getWebView(), for convenience. - private final WebView mWebView; - - // The Java objects that are exposed to JavaScript. - private TextToSpeechWrapper mTextToSpeech; - private CallbackHandler mCallback; - - // Lazily loaded helper objects. - private AccessibilityManager mAccessibilityManager; - private AccessibilityInjectorFallback mAccessibilityInjectorFallback; - private JSONObject mAccessibilityJSONObject; - - // Whether the accessibility script has been injected into the current page. - private boolean mAccessibilityScriptInjected; - - // Constants for determining script injection strategy. - private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1; - private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0; - @SuppressWarnings("unused") - private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1; - - // Alias for TTS API exposed to JavaScript. - private static final String ALIAS_TTS_JS_INTERFACE = "accessibility"; - - // Alias for traversal callback exposed to JavaScript. - private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal"; - - // Template for JavaScript that injects a screen-reader. - private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE = - "javascript:(function() {" + - " var chooser = document.createElement('script');" + - " chooser.type = 'text/javascript';" + - " chooser.src = '%1s';" + - " document.getElementsByTagName('head')[0].appendChild(chooser);" + - " })();"; - - // Template for JavaScript that performs AndroidVox actions. - private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE = - "(function() {" + - " if ((typeof(cvox) != 'undefined')" + - " && (cvox != null)" + - " && (typeof(cvox.ChromeVox) != 'undefined')" + - " && (cvox.ChromeVox != null)" + - " && (typeof(cvox.AndroidVox) != 'undefined')" + - " && (cvox.AndroidVox != null)" + - " && cvox.ChromeVox.isActive) {" + - " return cvox.AndroidVox.performAction('%1s');" + - " } else {" + - " return false;" + - " }" + - "})()"; - - // JS code used to shut down an active AndroidVox instance. - private static final String TOGGLE_CVOX_TEMPLATE = - "javascript:(function() {" + - " if ((typeof(cvox) != 'undefined')" + - " && (cvox != null)" + - " && (typeof(cvox.ChromeVox) != 'undefined')" + - " && (cvox.ChromeVox != null)" + - " && (typeof(cvox.ChromeVox.host) != 'undefined')" + - " && (cvox.ChromeVox.host != null)) {" + - " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" + - " }" + - "})();"; - - /** - * Creates an instance of the AccessibilityInjector based on - * {@code webViewClassic}. - * - * @param webViewClassic The WebViewClassic that this AccessibilityInjector - * manages. - */ - public AccessibilityInjector(WebViewClassic webViewClassic) { - mWebViewClassic = webViewClassic; - mWebView = webViewClassic.getWebView(); - mContext = webViewClassic.getContext(); - mAccessibilityManager = AccessibilityManager.getInstance(mContext); - } - - /** - * If JavaScript is enabled, pauses or resumes AndroidVox. - * - * @param enabled Whether feedback should be enabled. - */ - public void toggleAccessibilityFeedback(boolean enabled) { - if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) { - return; - } - - toggleAndroidVox(enabled); - - if (!enabled && (mTextToSpeech != null)) { - mTextToSpeech.stop(); - } - } - - /** - * Attempts to load scripting interfaces for accessibility. - * <p> - * This should only be called before a page loads. - */ - public void addAccessibilityApisIfNecessary() { - if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) { - return; - } - - addTtsApis(); - addCallbackApis(); - } - - /** - * Attempts to unload scripting interfaces for accessibility. - * <p> - * This should only be called before a page loads. - */ - private void removeAccessibilityApisIfNecessary() { - removeTtsApis(); - removeCallbackApis(); - } - - /** - * Destroys this accessibility injector. - */ - public void destroy() { - if (mTextToSpeech != null) { - mTextToSpeech.shutdown(); - mTextToSpeech = null; - } - - if (mCallback != null) { - mCallback = null; - } - } - - private void toggleAndroidVox(boolean state) { - if (!mAccessibilityScriptInjected) { - return; - } - - final String code = String.format(TOGGLE_CVOX_TEMPLATE, state); - mWebView.loadUrl(code); - } - - /** - * Initializes an {@link AccessibilityNodeInfo} with the actions and - * movement granularity levels supported by this - * {@link AccessibilityInjector}. - * <p> - * If an action identifier is added in this method, this - * {@link AccessibilityInjector} should also return {@code true} from - * {@link #supportsAccessibilityAction(int)}. - * </p> - * - * @param info The info to initialize. - * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) - */ - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH - | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE); - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT); - info.addAction(AccessibilityNodeInfo.ACTION_CLICK); - info.setClickable(true); - } - - /** - * Returns {@code true} if this {@link AccessibilityInjector} should handle - * the specified action. - * - * @param action An accessibility action identifier. - * @return {@code true} if this {@link AccessibilityInjector} should handle - * the specified action. - */ - public boolean supportsAccessibilityAction(int action) { - switch (action) { - case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: - case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: - case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_CLICK: - return true; - default: - return false; - } - } - - /** - * Performs the specified accessibility action. - * - * @param action The identifier of the action to perform. - * @param arguments The action arguments, or {@code null} if no arguments. - * @return {@code true} if the action was successful. - * @see View#performAccessibilityAction(int, Bundle) - */ - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (!isAccessibilityEnabled()) { - mAccessibilityScriptInjected = false; - toggleFallbackAccessibilityInjector(false); - return false; - } - - if (mAccessibilityScriptInjected) { - return sendActionToAndroidVox(action, arguments); - } - - if (mAccessibilityInjectorFallback != null) { - return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments); - } - - return false; - } - - /** - * Attempts to handle key events when accessibility is turned on. - * - * @param event The key event to handle. - * @return {@code true} if the event was handled. - */ - public boolean handleKeyEventIfNecessary(KeyEvent event) { - if (!isAccessibilityEnabled()) { - mAccessibilityScriptInjected = false; - toggleFallbackAccessibilityInjector(false); - return false; - } - - if (mAccessibilityScriptInjected) { - // if an accessibility script is injected we delegate to it the key - // handling. this script is a screen reader which is a fully fledged - // solution for blind users to navigate in and interact with web - // pages. - if (event.getAction() == KeyEvent.ACTION_UP) { - mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event); - } else if (event.getAction() == KeyEvent.ACTION_DOWN) { - mWebViewClassic.sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event); - } else { - return false; - } - - return true; - } - - if (mAccessibilityInjectorFallback != null) { - // if an accessibility injector is present (no JavaScript enabled or - // the site opts out injecting our JavaScript screen reader) we let - // it decide whether to act on and consume the event. - return mAccessibilityInjectorFallback.onKeyEvent(event); - } - - return false; - } - - /** - * Attempts to handle selection change events when accessibility is using a - * non-JavaScript method. - * <p> - * This must not be called from the main thread. - * - * @param selection The selection string. - * @param token The selection request token. - */ - public void onSelectionStringChangedWebCoreThread(String selection, int token) { - if (mAccessibilityInjectorFallback != null) { - mAccessibilityInjectorFallback.onSelectionStringChangedWebCoreThread(selection, token); - } - } - - /** - * Prepares for injecting accessibility scripts into a new page. - * - * @param url The URL that will be loaded. - */ - public void onPageStarted(String url) { - mAccessibilityScriptInjected = false; - if (DEBUG) { - Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page"); - } - addAccessibilityApisIfNecessary(); - } - - /** - * Attempts to inject the accessibility script using a {@code <script>} tag. - * <p> - * This should be called after a page has finished loading. - * </p> - * - * @param url The URL that just finished loading. - */ - public void onPageFinished(String url) { - if (!isAccessibilityEnabled()) { - toggleFallbackAccessibilityInjector(false); - return; - } - - toggleFallbackAccessibilityInjector(true); - - if (shouldInjectJavaScript(url)) { - // If we're supposed to use the JS screen reader, request a - // callback to confirm that CallbackHandler is working. - if (DEBUG) { - Log.d(TAG, "[" + mWebView.hashCode() + "] Request callback "); - } - - mCallback.requestCallback(mWebView, mInjectScriptRunnable); - } - } - - /** - * Runnable used to inject the JavaScript-based screen reader if the - * {@link CallbackHandler} API was successfully exposed to JavaScript. - */ - private Runnable mInjectScriptRunnable = new Runnable() { - @Override - public void run() { - if (DEBUG) { - Log.d(TAG, "[" + mWebView.hashCode() + "] Received callback"); - } - - injectJavaScript(); - } - }; - - /** - * Called by {@link #mInjectScriptRunnable} to inject the JavaScript-based - * screen reader after confirming that the {@link CallbackHandler} API is - * functional. - */ - private void injectJavaScript() { - toggleFallbackAccessibilityInjector(false); - - if (!mAccessibilityScriptInjected) { - mAccessibilityScriptInjected = true; - final String injectionUrl = getScreenReaderInjectionUrl(); - mWebView.loadUrl(injectionUrl); - if (DEBUG) { - Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView"); - } - } else { - if (DEBUG) { - Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice"); - } - } - } - - /** - * Adjusts the accessibility injection state to reflect changes in the - * JavaScript enabled state. - * - * @param enabled Whether JavaScript is enabled. - */ - public void updateJavaScriptEnabled(boolean enabled) { - if (enabled) { - addAccessibilityApisIfNecessary(); - } else { - removeAccessibilityApisIfNecessary(); - } - - // We have to reload the page after adding or removing APIs. - mWebView.reload(); - } - - /** - * Toggles the non-JavaScript method for handling accessibility. - * - * @param enabled {@code true} to enable the non-JavaScript method, or - * {@code false} to disable it. - */ - private void toggleFallbackAccessibilityInjector(boolean enabled) { - if (enabled && (mAccessibilityInjectorFallback == null)) { - mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic); - } else { - mAccessibilityInjectorFallback = null; - } - } - - /** - * Determines whether it's okay to inject JavaScript into a given URL. - * - * @param url The URL to check. - * @return {@code true} if JavaScript should be injected, {@code false} if a - * non-JavaScript method should be used. - */ - private boolean shouldInjectJavaScript(String url) { - // Respect the WebView's JavaScript setting. - if (!isJavaScriptEnabled()) { - return false; - } - - // Allow the page to opt out of Accessibility script injection. - if (getAxsUrlParameterValue(url) == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) { - return false; - } - - // The user must explicitly enable Accessibility script injection. - if (!isScriptInjectionEnabled()) { - return false; - } - - return true; - } - - /** - * @return {@code true} if the user has explicitly enabled Accessibility - * script injection. - */ - private boolean isScriptInjectionEnabled() { - final int injectionSetting = Settings.Secure.getInt( - mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0); - return (injectionSetting == 1); - } - - /** - * Attempts to initialize and add interfaces for TTS, if that hasn't already - * been done. - */ - private void addTtsApis() { - if (mTextToSpeech == null) { - mTextToSpeech = new TextToSpeechWrapper(mContext); - } - - mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE); - } - - /** - * Attempts to shutdown and remove interfaces for TTS, if that hasn't - * already been done. - */ - private void removeTtsApis() { - if (mTextToSpeech != null) { - mTextToSpeech.stop(); - mTextToSpeech.shutdown(); - mTextToSpeech = null; - } - - mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE); - } - - private void addCallbackApis() { - if (mCallback == null) { - mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE); - } - - mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE); - } - - private void removeCallbackApis() { - if (mCallback != null) { - mCallback = null; - } - - mWebView.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE); - } - - /** - * Returns the script injection preference requested by the URL, or - * {@link #ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED} if the page has no - * preference. - * - * @param url The URL to check. - * @return A script injection preference. - */ - private int getAxsUrlParameterValue(String url) { - if (url == null) { - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED; - } - - try { - final List<NameValuePair> params = URLEncodedUtils.parse(new URI(url), null); - - for (NameValuePair param : params) { - if ("axs".equals(param.getName())) { - return verifyInjectionValue(param.getValue()); - } - } - } catch (URISyntaxException e) { - // Do nothing. - } catch (IllegalArgumentException e) { - // Catch badly-formed URLs. - } - - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED; - } - - private int verifyInjectionValue(String value) { - try { - final int parsed = Integer.parseInt(value); - - switch (parsed) { - case ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT: - return ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT; - case ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED: - return ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED; - } - } catch (NumberFormatException e) { - // Do nothing. - } - - return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED; - } - - /** - * @return The URL for injecting the screen reader. - */ - private String getScreenReaderInjectionUrl() { - final String screenReaderUrl = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL); - return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl); - } - - /** - * @return {@code true} if JavaScript is enabled in the {@link WebView} - * settings. - */ - private boolean isJavaScriptEnabled() { - final WebSettings settings = mWebView.getSettings(); - if (settings == null) { - return false; - } - - return settings.getJavaScriptEnabled(); - } - - /** - * @return {@code true} if accessibility is enabled. - */ - private boolean isAccessibilityEnabled() { - return mAccessibilityManager.isEnabled(); - } - - /** - * Packs an accessibility action into a JSON object and sends it to AndroidVox. - * - * @param action The action identifier. - * @param arguments The action arguments, if applicable. - * @return The result of the action. - */ - private boolean sendActionToAndroidVox(int action, Bundle arguments) { - if (mAccessibilityJSONObject == null) { - mAccessibilityJSONObject = new JSONObject(); - } else { - // Remove all keys from the object. - final Iterator<?> keys = mAccessibilityJSONObject.keys(); - while (keys.hasNext()) { - keys.next(); - keys.remove(); - } - } - - try { - mAccessibilityJSONObject.accumulate("action", action); - - switch (action) { - case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: - case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: - if (arguments != null) { - final int granularity = arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); - mAccessibilityJSONObject.accumulate("granularity", granularity); - } - break; - case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: - if (arguments != null) { - final String element = arguments.getString( - AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING); - mAccessibilityJSONObject.accumulate("element", element); - } - break; - } - } catch (JSONException e) { - return false; - } - - final String jsonString = mAccessibilityJSONObject.toString(); - final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString); - return mCallback.performAction(mWebView, jsCode); - } - - /** - * Used to protect the TextToSpeech class, only exposing the methods we want to expose. - */ - private static class TextToSpeechWrapper { - private static final String WRAP_TAG = TextToSpeechWrapper.class.getSimpleName(); - - /** Lock used to control access to the TextToSpeech object. */ - private final Object mTtsLock = new Object(); - - private final HashMap<String, String> mTtsParams; - private final TextToSpeech mTextToSpeech; - - /** - * Whether this wrapper is ready to speak. If this is {@code true} then - * {@link #mShutdown} is guaranteed to be {@code false}. - */ - private volatile boolean mReady; - - /** - * Whether this wrapper was shut down. If this is {@code true} then - * {@link #mReady} is guaranteed to be {@code false}. - */ - private volatile boolean mShutdown; - - public TextToSpeechWrapper(Context context) { - if (DEBUG) { - Log.d(WRAP_TAG, "[" + hashCode() + "] Initializing text-to-speech on thread " - + Thread.currentThread().getId() + "..."); - } - - final String pkgName = context.getPackageName(); - - mReady = false; - mShutdown = false; - - mTtsParams = new HashMap<String, String>(); - mTtsParams.put(Engine.KEY_PARAM_UTTERANCE_ID, WRAP_TAG); - - mTextToSpeech = new TextToSpeech( - context, mInitListener, null, pkgName + ".**webview**", true); - mTextToSpeech.setOnUtteranceProgressListener(mErrorListener); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public boolean isSpeaking() { - synchronized (mTtsLock) { - if (!mReady) { - return false; - } - - return mTextToSpeech.isSpeaking(); - } - } - - @JavascriptInterface - @SuppressWarnings("unused") - public int speak(String text, int queueMode, HashMap<String, String> params) { - synchronized (mTtsLock) { - if (!mReady) { - if (DEBUG) { - Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to speak before TTS init"); - } - return TextToSpeech.ERROR; - } else { - if (DEBUG) { - Log.i(WRAP_TAG, "[" + hashCode() + "] Speak called from JS binder"); - } - } - - return mTextToSpeech.speak(text, queueMode, params); - } - } - - @JavascriptInterface - @SuppressWarnings("unused") - public int stop() { - synchronized (mTtsLock) { - if (!mReady) { - if (DEBUG) { - Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to stop before initialize"); - } - return TextToSpeech.ERROR; - } else { - if (DEBUG) { - Log.i(WRAP_TAG, "[" + hashCode() + "] Stop called from JS binder"); - } - } - - return mTextToSpeech.stop(); - } - } - - @SuppressWarnings("unused") - protected void shutdown() { - synchronized (mTtsLock) { - if (!mReady) { - if (DEBUG) { - Log.w(WRAP_TAG, "[" + hashCode() + "] Called shutdown before initialize"); - } - } else { - if (DEBUG) { - Log.i(WRAP_TAG, "[" + hashCode() + "] Shutting down text-to-speech from " - + "thread " + Thread.currentThread().getId() + "..."); - } - } - mShutdown = true; - mReady = false; - mTextToSpeech.shutdown(); - } - } - - private final OnInitListener mInitListener = new OnInitListener() { - @Override - public void onInit(int status) { - synchronized (mTtsLock) { - if (!mShutdown && (status == TextToSpeech.SUCCESS)) { - if (DEBUG) { - Log.d(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() - + "] Initialized successfully"); - } - mReady = true; - } else { - if (DEBUG) { - Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() - + "] Failed to initialize"); - } - mReady = false; - } - } - } - }; - - private final UtteranceProgressListener mErrorListener = new UtteranceProgressListener() { - @Override - public void onStart(String utteranceId) { - // Do nothing. - } - - @Override - public void onError(String utteranceId) { - if (DEBUG) { - Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() - + "] Failed to speak utterance"); - } - } - - @Override - public void onDone(String utteranceId) { - // Do nothing. - } - }; - } - - /** - * Exposes result interface to JavaScript. - */ - private static class CallbackHandler { - private static final String JAVASCRIPT_ACTION_TEMPLATE = - "javascript:(function() { %s.onResult(%d, %s); })();"; - - // Time in milliseconds to wait for a result before failing. - private static final long RESULT_TIMEOUT = 5000; - - private final AtomicInteger mResultIdCounter = new AtomicInteger(); - private final Object mResultLock = new Object(); - private final String mInterfaceName; - private final Handler mMainHandler; - - private Runnable mCallbackRunnable; - - private boolean mResult = false; - private int mResultId = -1; - - private CallbackHandler(String interfaceName) { - mInterfaceName = interfaceName; - mMainHandler = new Handler(); - } - - /** - * Performs an action and attempts to wait for a result. - * - * @param webView The WebView to perform the action on. - * @param code JavaScript code that evaluates to a result. - * @return The result of the action, or false if it timed out. - */ - private boolean performAction(WebView webView, String code) { - final int resultId = mResultIdCounter.getAndIncrement(); - final String url = String.format( - JAVASCRIPT_ACTION_TEMPLATE, mInterfaceName, resultId, code); - webView.loadUrl(url); - - return getResultAndClear(resultId); - } - - /** - * Gets the result of a request to perform an accessibility action. - * - * @param resultId The result id to match the result with the request. - * @return The result of the request. - */ - private boolean getResultAndClear(int resultId) { - synchronized (mResultLock) { - final boolean success = waitForResultTimedLocked(resultId); - final boolean result = success ? mResult : false; - clearResultLocked(); - return result; - } - } - - /** - * Clears the result state. - */ - private void clearResultLocked() { - mResultId = -1; - mResult = false; - } - - /** - * Waits up to a given bound for a result of a request and returns it. - * - * @param resultId The result id to match the result with the request. - * @return Whether the result was received. - */ - private boolean waitForResultTimedLocked(int resultId) { - final long startTimeMillis = SystemClock.uptimeMillis(); - - if (DEBUG) { - Log.d(TAG, "Waiting for CVOX result with ID " + resultId + "..."); - } - - while (true) { - // Fail if we received a callback from the future. - if (mResultId > resultId) { - if (DEBUG) { - Log.w(TAG, "Aborted CVOX result"); - } - return false; - } - - final long elapsedTimeMillis = (SystemClock.uptimeMillis() - startTimeMillis); - - // Succeed if we received the callback we were expecting. - if (DEBUG) { - Log.w(TAG, "Check " + mResultId + " versus expected " + resultId); - } - if (mResultId == resultId) { - if (DEBUG) { - Log.w(TAG, "Received CVOX result after " + elapsedTimeMillis + " ms"); - } - return true; - } - - final long waitTimeMillis = (RESULT_TIMEOUT - elapsedTimeMillis); - - // Fail if we've already exceeded the timeout. - if (waitTimeMillis <= 0) { - if (DEBUG) { - Log.w(TAG, "Timed out while waiting for CVOX result"); - } - return false; - } - - try { - if (DEBUG) { - Log.w(TAG, "Start waiting..."); - } - mResultLock.wait(waitTimeMillis); - } catch (InterruptedException ie) { - if (DEBUG) { - Log.w(TAG, "Interrupted while waiting for CVOX result"); - } - } - } - } - - /** - * Callback exposed to JavaScript. Handles returning the result of a - * request to a waiting (or potentially timed out) thread. - * - * @param id The result id of the request as a {@link String}. - * @param result The result of the request as a {@link String}. - */ - @JavascriptInterface - @SuppressWarnings("unused") - public void onResult(String id, String result) { - if (DEBUG) { - Log.w(TAG, "Saw CVOX result of '" + result + "' for ID " + id); - } - final int resultId; - - try { - resultId = Integer.parseInt(id); - } catch (NumberFormatException e) { - return; - } - - synchronized (mResultLock) { - if (resultId > mResultId) { - mResult = Boolean.parseBoolean(result); - mResultId = resultId; - } else { - if (DEBUG) { - Log.w(TAG, "Result with ID " + resultId + " was stale vesus " + mResultId); - } - } - mResultLock.notifyAll(); - } - } - - /** - * Requests a callback to ensure that the JavaScript interface for this - * object has been added successfully. - * - * @param webView The web view to request a callback from. - * @param callbackRunnable Runnable to execute if a callback is received. - */ - public void requestCallback(WebView webView, Runnable callbackRunnable) { - mCallbackRunnable = callbackRunnable; - - webView.loadUrl("javascript:(function() { " + mInterfaceName + ".callback(); })();"); - } - - @JavascriptInterface - @SuppressWarnings("unused") - public void callback() { - if (mCallbackRunnable != null) { - mMainHandler.post(mCallbackRunnable); - mCallbackRunnable = null; - } - } - } -} diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java deleted file mode 100644 index 40cc4e9..0000000 --- a/core/java/android/webkit/AccessibilityInjectorFallback.java +++ /dev/null @@ -1,636 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.os.Bundle; -import android.provider.Settings; -import android.text.TextUtils; -import android.text.TextUtils.SimpleStringSplitter; -import android.util.Log; -import android.view.KeyEvent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.webkit.WebViewCore.EventHub; - -import com.android.internal.os.SomeArgs; - -import java.util.ArrayList; - -/** - * This class injects accessibility into WebViews with disabled JavaScript or - * WebViews with enabled JavaScript but for which we have no accessibility - * script to inject. - * </p> - * Note: To avoid changes in the framework upon changing the available - * navigation axis, or reordering the navigation axis, or changing - * the key bindings, or defining sequence of actions to be bound to - * a given key this class is navigation axis agnostic. It is only - * aware of one navigation axis which is in fact the default behavior - * of webViews while using the DPAD/TrackBall. - * </p> - * In general a key binding is a mapping from modifiers + key code to - * a sequence of actions. For more detail how to specify key bindings refer to - * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}. - * </p> - * The possible actions are invocations to - * {@link #setCurrentAxis(int, boolean, String)}, or - * {@link #traverseGivenAxis(int, int, boolean, String, boolean)} - * {@link #performAxisTransition(int, int, boolean, String)} - * referred via the values of: - * {@link #ACTION_SET_CURRENT_AXIS}, - * {@link #ACTION_TRAVERSE_CURRENT_AXIS}, - * {@link #ACTION_TRAVERSE_GIVEN_AXIS}, - * {@link #ACTION_PERFORM_AXIS_TRANSITION}, - * respectively. - * The arguments for the action invocation are specified as offset - * hexademical pairs. Note the last argument of the invocation - * should NOT be specified in the binding as it is provided by - * this class. For details about the key binding implementation - * refer to {@link AccessibilityWebContentKeyBinding}. - */ -class AccessibilityInjectorFallback { - private static final String LOG_TAG = "AccessibilityInjector"; - - private static final boolean DEBUG = true; - - private static final int ACTION_SET_CURRENT_AXIS = 0; - private static final int ACTION_TRAVERSE_CURRENT_AXIS = 1; - private static final int ACTION_TRAVERSE_GIVEN_AXIS = 2; - private static final int ACTION_PERFORM_AXIS_TRANSITION = 3; - private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4; - - /** Timeout after which asynchronous granular movement is aborted. */ - private static final int MODIFY_SELECTION_TIMEOUT = 500; - - // WebView navigation axes from WebViewCore.h, plus an additional axis for - // the default behavior. - private static final int NAVIGATION_AXIS_CHARACTER = 0; - private static final int NAVIGATION_AXIS_WORD = 1; - private static final int NAVIGATION_AXIS_SENTENCE = 2; - @SuppressWarnings("unused") - private static final int NAVIGATION_AXIS_HEADING = 3; - @SuppressWarnings("unused") - private static final int NAVIGATION_AXIS_SIBLING = 4; - @SuppressWarnings("unused") - private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5; - private static final int NAVIGATION_AXIS_DOCUMENT = 6; - private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7; - - // WebView navigation directions from WebViewCore.h. - private static final int NAVIGATION_DIRECTION_BACKWARD = 0; - private static final int NAVIGATION_DIRECTION_FORWARD = 1; - - // these are the same for all instances so make them process wide - private static ArrayList<AccessibilityWebContentKeyBinding> sBindings = - new ArrayList<AccessibilityWebContentKeyBinding>(); - - // handle to the WebViewClassic this injector is associated with. - private final WebViewClassic mWebView; - private final WebView mWebViewInternal; - - // Event scheduled for sending as soon as we receive the selected text. - private AccessibilityEvent mScheduledEvent; - - // Token required to send the scheduled event. - private int mScheduledToken = 0; - - // the current traversal axis - private int mCurrentAxis = 2; // sentence - - // we need to consume the up if we have handled the last down - private boolean mLastDownEventHandled; - - // getting two empty selection strings in a row we let the WebView handle the event - private boolean mIsLastSelectionStringNull; - - // keep track of last direction - private int mLastDirection; - - // Lock used for asynchronous selection callback. - private final Object mCallbackLock = new Object(); - - // Whether the asynchronous selection callback was received. - private boolean mCallbackReceived; - - // Whether the asynchronous selection callback succeeded. - private boolean mCallbackResult; - - /** - * Creates a new injector associated with a given {@link WebViewClassic}. - * - * @param webView The associated WebViewClassic. - */ - public AccessibilityInjectorFallback(WebViewClassic webView) { - mWebView = webView; - mWebViewInternal = mWebView.getWebView(); - ensureWebContentKeyBindings(); - } - - /** - * Processes a key down <code>event</code>. - * - * @return True if the event was processed. - */ - public boolean onKeyEvent(KeyEvent event) { - // We do not handle ENTER in any circumstances. - if (isEnterActionKey(event.getKeyCode())) { - return false; - } - - if (event.getAction() == KeyEvent.ACTION_UP) { - return mLastDownEventHandled; - } - - mLastDownEventHandled = false; - - AccessibilityWebContentKeyBinding binding = null; - for (AccessibilityWebContentKeyBinding candidate : sBindings) { - if (event.getKeyCode() == candidate.getKeyCode() - && event.hasModifiers(candidate.getModifiers())) { - binding = candidate; - break; - } - } - - if (binding == null) { - return false; - } - - for (int i = 0, count = binding.getActionCount(); i < count; i++) { - int actionCode = binding.getActionCode(i); - String contentDescription = Integer.toHexString(binding.getAction(i)); - switch (actionCode) { - case ACTION_SET_CURRENT_AXIS: - int axis = binding.getFirstArgument(i); - boolean sendEvent = (binding.getSecondArgument(i) == 1); - setCurrentAxis(axis, sendEvent, contentDescription); - mLastDownEventHandled = true; - break; - case ACTION_TRAVERSE_CURRENT_AXIS: - int direction = binding.getFirstArgument(i); - // on second null selection string in same direction - WebView handles the event - if (direction == mLastDirection && mIsLastSelectionStringNull) { - mIsLastSelectionStringNull = false; - return false; - } - mLastDirection = direction; - sendEvent = (binding.getSecondArgument(i) == 1); - mLastDownEventHandled = traverseGivenAxis( - direction, mCurrentAxis, sendEvent, contentDescription, false); - break; - case ACTION_TRAVERSE_GIVEN_AXIS: - direction = binding.getFirstArgument(i); - // on second null selection string in same direction => WebView handle the event - if (direction == mLastDirection && mIsLastSelectionStringNull) { - mIsLastSelectionStringNull = false; - return false; - } - mLastDirection = direction; - axis = binding.getSecondArgument(i); - sendEvent = (binding.getThirdArgument(i) == 1); - traverseGivenAxis(direction, axis, sendEvent, contentDescription, false); - mLastDownEventHandled = true; - break; - case ACTION_PERFORM_AXIS_TRANSITION: - int fromAxis = binding.getFirstArgument(i); - int toAxis = binding.getSecondArgument(i); - sendEvent = (binding.getThirdArgument(i) == 1); - performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription); - mLastDownEventHandled = true; - break; - case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS: - // This is a special case since we treat the default WebView navigation - // behavior as one of the possible navigation axis the user can use. - // If we are not on the default WebView navigation axis this is NOP. - if (mCurrentAxis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) { - // While WebVew handles navigation we do not get null selection - // strings so do not check for that here as the cases above. - mLastDirection = binding.getFirstArgument(i); - sendEvent = (binding.getSecondArgument(i) == 1); - traverseGivenAxis(mLastDirection, NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR, - sendEvent, contentDescription, false); - mLastDownEventHandled = false; - } else { - mLastDownEventHandled = true; - } - break; - default: - Log.w(LOG_TAG, "Unknown action code: " + actionCode); - } - } - - return mLastDownEventHandled; - } - - /** - * Set the current navigation axis. - * - * @param axis The axis to set. - * @param sendEvent Whether to send an accessibility event to - * announce the change. - */ - private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) { - mCurrentAxis = axis; - if (sendEvent) { - final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent( - AccessibilityEvent.TYPE_ANNOUNCEMENT); - event.getText().add(String.valueOf(axis)); - event.setContentDescription(contentDescription); - sendAccessibilityEvent(event); - } - } - - /** - * Performs conditional transition one axis to another. - * - * @param fromAxis The axis which must be the current for the transition to occur. - * @param toAxis The axis to which to transition. - * @param sendEvent Flag if to send an event to announce successful transition. - * @param contentDescription A description of the performed action. - */ - private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent, - String contentDescription) { - if (mCurrentAxis == fromAxis) { - setCurrentAxis(toAxis, sendEvent, contentDescription); - } - } - - boolean performAccessibilityAction(int action, Bundle arguments) { - switch (action) { - case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: - case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: { - final int direction = getDirectionForAction(action); - final int axis = getAxisForGranularity(arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT)); - return traverseGivenAxis(direction, axis, true, null, true); - } - case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: { - final int direction = getDirectionForAction(action); - // TODO: Add support for moving by object. - final int axis = NAVIGATION_AXIS_SENTENCE; - return traverseGivenAxis(direction, axis, true, null, true); - } - default: - return false; - } - } - - /** - * Returns the {@link WebView}-defined direction for the given - * {@link AccessibilityNodeInfo}-defined action. - * - * @param action An accessibility action identifier. - * @return A web view navigation direction. - */ - private static int getDirectionForAction(int action) { - switch (action) { - case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: - return NAVIGATION_DIRECTION_FORWARD; - case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: - case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: - return NAVIGATION_DIRECTION_BACKWARD; - default: - return -1; - } - } - - /** - * Returns the {@link WebView}-defined axis for the given - * {@link AccessibilityNodeInfo}-defined granularity. - * - * @param granularity An accessibility granularity identifier. - * @return A web view navigation axis. - */ - private static int getAxisForGranularity(int granularity) { - switch (granularity) { - case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: - return NAVIGATION_AXIS_CHARACTER; - case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: - return NAVIGATION_AXIS_WORD; - case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE: - return NAVIGATION_AXIS_SENTENCE; - case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH: - // TODO: This should map to object once we implement it. - return NAVIGATION_AXIS_SENTENCE; - case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE: - return NAVIGATION_AXIS_DOCUMENT; - default: - return -1; - } - } - - /** - * Traverse the document along the given navigation axis. - * - * @param direction The direction of traversal. - * @param axis The axis along which to traverse. - * @param sendEvent Whether to send an accessibility event to - * announce the change. - * @param contentDescription A description of the performed action. - */ - private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent, - String contentDescription, boolean sychronous) { - final WebViewCore webViewCore = mWebView.getWebViewCore(); - if (webViewCore == null) { - return false; - } - - if (sendEvent) { - final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent( - AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); - // The text will be set upon receiving the selection string. - event.setContentDescription(contentDescription); - mScheduledEvent = event; - mScheduledToken++; - } - - // if the axis is the default let WebView handle the event which will - // result in cursor ring movement and selection of its content - if (axis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) { - return false; - } - - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = direction; - args.argi2 = axis; - args.argi3 = mScheduledToken; - - // If we don't need synchronous results, just return true. - if (!sychronous) { - webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args); - return true; - } - - final boolean callbackResult; - - synchronized (mCallbackLock) { - mCallbackReceived = false; - - // Asynchronously changes the selection in WebView, which responds by - // calling onSelectionStringChanged(). - webViewCore.sendMessage(EventHub.MODIFY_SELECTION, args); - - try { - mCallbackLock.wait(MODIFY_SELECTION_TIMEOUT); - } catch (InterruptedException e) { - // Do nothing. - } - - callbackResult = mCallbackResult; - } - - return (mCallbackReceived && callbackResult); - } - - /* package */ void onSelectionStringChangedWebCoreThread( - final String selection, final int token) { - synchronized (mCallbackLock) { - mCallbackReceived = true; - mCallbackResult = (selection != null); - mCallbackLock.notifyAll(); - } - - // Managing state and sending events must take place on the UI thread. - mWebViewInternal.post(new Runnable() { - @Override - public void run() { - onSelectionStringChangedMainThread(selection, token); - } - }); - } - - private void onSelectionStringChangedMainThread(String selection, int token) { - if (DEBUG) { - Log.d(LOG_TAG, "Selection string: " + selection); - } - - if (token != mScheduledToken) { - if (DEBUG) { - Log.d(LOG_TAG, "Selection string has incorrect token: " + token); - } - return; - } - - mIsLastSelectionStringNull = (selection == null); - - final AccessibilityEvent event = mScheduledEvent; - mScheduledEvent = null; - - if ((event != null) && (selection != null)) { - event.getText().add(selection); - event.setFromIndex(0); - event.setToIndex(selection.length()); - sendAccessibilityEvent(event); - } - } - - /** - * Sends an {@link AccessibilityEvent}. - * - * @param event The event to send. - */ - private void sendAccessibilityEvent(AccessibilityEvent event) { - if (DEBUG) { - Log.d(LOG_TAG, "Dispatching: " + event); - } - // accessibility may be disabled while waiting for the selection string - AccessibilityManager accessibilityManager = - AccessibilityManager.getInstance(mWebView.getContext()); - if (accessibilityManager.isEnabled()) { - accessibilityManager.sendAccessibilityEvent(event); - } - } - - /** - * @return An accessibility event whose members are populated except its - * text and content description. - */ - private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) { - AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - mWebViewInternal.onInitializeAccessibilityEvent(event); - return event; - } - - /** - * Ensures that the Web content key bindings are loaded. - */ - private void ensureWebContentKeyBindings() { - if (sBindings.size() > 0) { - return; - } - - String webContentKeyBindingsString = Settings.Secure.getString( - mWebView.getContext().getContentResolver(), - Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS); - - SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';'); - semiColonSplitter.setString(webContentKeyBindingsString); - - while (semiColonSplitter.hasNext()) { - String bindingString = semiColonSplitter.next(); - if (TextUtils.isEmpty(bindingString)) { - Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " - + webContentKeyBindingsString); - continue; - } - String[] keyValueArray = bindingString.split("="); - if (keyValueArray.length != 2) { - Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " + bindingString); - continue; - } - try { - long keyCodeAndModifiers = Long.decode(keyValueArray[0].trim()); - String[] actionStrings = keyValueArray[1].split(":"); - int[] actions = new int[actionStrings.length]; - for (int i = 0, count = actions.length; i < count; i++) { - actions[i] = Integer.decode(actionStrings[i].trim()); - } - sBindings.add(new AccessibilityWebContentKeyBinding(keyCodeAndModifiers, actions)); - } catch (NumberFormatException nfe) { - Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString); - } - } - } - - private boolean isEnterActionKey(int keyCode) { - return keyCode == KeyEvent.KEYCODE_DPAD_CENTER - || keyCode == KeyEvent.KEYCODE_ENTER - || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER; - } - - /** - * Represents a web content key-binding. - */ - private static final class AccessibilityWebContentKeyBinding { - - private static final int MODIFIERS_OFFSET = 32; - private static final long MODIFIERS_MASK = 0xFFFFFFF00000000L; - - private static final int KEY_CODE_OFFSET = 0; - private static final long KEY_CODE_MASK = 0x00000000FFFFFFFFL; - - private static final int ACTION_OFFSET = 24; - private static final int ACTION_MASK = 0xFF000000; - - private static final int FIRST_ARGUMENT_OFFSET = 16; - private static final int FIRST_ARGUMENT_MASK = 0x00FF0000; - - private static final int SECOND_ARGUMENT_OFFSET = 8; - private static final int SECOND_ARGUMENT_MASK = 0x0000FF00; - - private static final int THIRD_ARGUMENT_OFFSET = 0; - private static final int THIRD_ARGUMENT_MASK = 0x000000FF; - - private final long mKeyCodeAndModifiers; - - private final int [] mActionSequence; - - /** - * @return The key code of the binding key. - */ - public int getKeyCode() { - return (int) ((mKeyCodeAndModifiers & KEY_CODE_MASK) >> KEY_CODE_OFFSET); - } - - /** - * @return The meta state of the binding key. - */ - public int getModifiers() { - return (int) ((mKeyCodeAndModifiers & MODIFIERS_MASK) >> MODIFIERS_OFFSET); - } - - /** - * @return The number of actions in the key binding. - */ - public int getActionCount() { - return mActionSequence.length; - } - - /** - * @param index The action for a given action <code>index</code>. - */ - public int getAction(int index) { - return mActionSequence[index]; - } - - /** - * @param index The action code for a given action <code>index</code>. - */ - public int getActionCode(int index) { - return (mActionSequence[index] & ACTION_MASK) >> ACTION_OFFSET; - } - - /** - * @param index The first argument for a given action <code>index</code>. - */ - public int getFirstArgument(int index) { - return (mActionSequence[index] & FIRST_ARGUMENT_MASK) >> FIRST_ARGUMENT_OFFSET; - } - - /** - * @param index The second argument for a given action <code>index</code>. - */ - public int getSecondArgument(int index) { - return (mActionSequence[index] & SECOND_ARGUMENT_MASK) >> SECOND_ARGUMENT_OFFSET; - } - - /** - * @param index The third argument for a given action <code>index</code>. - */ - public int getThirdArgument(int index) { - return (mActionSequence[index] & THIRD_ARGUMENT_MASK) >> THIRD_ARGUMENT_OFFSET; - } - - /** - * Creates a new instance. - * @param keyCodeAndModifiers The key for the binding (key and modifiers). - * @param actionSequence The sequence of action for the binding. - */ - public AccessibilityWebContentKeyBinding(long keyCodeAndModifiers, int[] actionSequence) { - mKeyCodeAndModifiers = keyCodeAndModifiers; - mActionSequence = actionSequence; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("modifiers: "); - builder.append(getModifiers()); - builder.append(", keyCode: "); - builder.append(getKeyCode()); - builder.append(", actions["); - for (int i = 0, count = getActionCount(); i < count; i++) { - builder.append("{actionCode"); - builder.append(i); - builder.append(": "); - builder.append(getActionCode(i)); - builder.append(", firstArgument: "); - builder.append(getFirstArgument(i)); - builder.append(", secondArgument: "); - builder.append(getSecondArgument(i)); - builder.append(", thirdArgument: "); - builder.append(getThirdArgument(i)); - builder.append("}"); - } - builder.append("]"); - return builder.toString(); - } - } -} diff --git a/core/java/android/webkit/AutoCompletePopup.java b/core/java/android/webkit/AutoCompletePopup.java deleted file mode 100644 index c624ce4..0000000 --- a/core/java/android/webkit/AutoCompletePopup.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.Context; -import android.os.Handler; -import android.os.Message; -import android.text.Editable; -import android.view.KeyEvent; -import android.view.View; -import android.widget.AbsoluteLayout; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.Filter; -import android.widget.Filterable; -import android.widget.ListAdapter; -import android.widget.ListPopupWindow; -import android.widget.PopupWindow.OnDismissListener; - -class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener, - OnDismissListener{ - private static class AnchorView extends View { - AnchorView(Context context) { - super(context); - setFocusable(false); - setVisibility(INVISIBLE); - } - } - private static final int AUTOFILL_FORM = 100; - private boolean mIsAutoFillProfileSet; - private Handler mHandler; - private int mQueryId; - private ListPopupWindow mPopup; - private Filter mFilter; - private CharSequence mText; - private ListAdapter mAdapter; - private View mAnchor; - private WebViewClassic.WebViewInputConnection mInputConnection; - private WebViewClassic mWebView; - - public AutoCompletePopup(WebViewClassic webView, - WebViewClassic.WebViewInputConnection inputConnection) { - mInputConnection = inputConnection; - mWebView = webView; - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case AUTOFILL_FORM: - mWebView.autoFillForm(mQueryId); - break; - } - } - }; - } - - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (mPopup == null) { - return false; - } - if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) { - // special case for the back key, we do not even try to send it - // to the drop down list but instead, consume it immediately - if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { - KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState(); - if (state != null) { - state.startTracking(event, this); - } - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState(); - if (state != null) { - state.handleUpEvent(event); - } - if (event.isTracking() && !event.isCanceled()) { - mPopup.dismiss(); - return true; - } - } - } - if (mPopup.isShowing()) { - return mPopup.onKeyPreIme(keyCode, event); - } - return false; - } - - public void setText(CharSequence text) { - mText = text; - if (mFilter != null) { - mFilter.filter(text, this); - } - } - - public void setAutoFillQueryId(int queryId) { - mQueryId = queryId; - } - - public void clearAdapter() { - mAdapter = null; - mFilter = null; - if (mPopup != null) { - mPopup.dismiss(); - mPopup.setAdapter(null); - } - } - - public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { - ensurePopup(); - mPopup.setAdapter(adapter); - mAdapter = adapter; - if (adapter != null) { - mFilter = adapter.getFilter(); - mFilter.filter(mText, this); - } else { - mFilter = null; - } - resetRect(); - } - - public void resetRect() { - ensurePopup(); - int left = mWebView.contentToViewX(mWebView.mEditTextContentBounds.left); - int right = mWebView.contentToViewX(mWebView.mEditTextContentBounds.right); - int width = right - left; - mPopup.setWidth(width); - - int bottom = mWebView.contentToViewY(mWebView.mEditTextContentBounds.bottom); - int top = mWebView.contentToViewY(mWebView.mEditTextContentBounds.top); - int height = bottom - top; - - AbsoluteLayout.LayoutParams lp = - (AbsoluteLayout.LayoutParams) mAnchor.getLayoutParams(); - boolean needsUpdate = false; - if (null == lp) { - lp = new AbsoluteLayout.LayoutParams(width, height, left, top); - } else { - if ((lp.x != left) || (lp.y != top) || (lp.width != width) - || (lp.height != height)) { - needsUpdate = true; - lp.x = left; - lp.y = top; - lp.width = width; - lp.height = height; - } - } - if (needsUpdate) { - mAnchor.setLayoutParams(lp); - } - if (mPopup.isShowing()) { - mPopup.show(); // update its position - } - } - - // AdapterView.OnItemClickListener implementation - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (mPopup == null) { - return; - } - if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) { - mText = ""; - pushTextToInputConnection(); - // Blank out the text box while we wait for WebCore to fill the form. - if (mIsAutoFillProfileSet) { - // Call a webview method to tell WebCore to autofill the form. - mWebView.autoFillForm(mQueryId); - } else { - // There is no autofill profile setup yet and the user has - // elected to try and set one up. Call through to the - // embedder to action that. - WebChromeClient webChromeClient = mWebView.getWebChromeClient(); - if (webChromeClient != null) { - webChromeClient.setupAutoFill( - mHandler.obtainMessage(AUTOFILL_FORM)); - } - } - } else { - Object selectedItem; - if (position < 0) { - selectedItem = mPopup.getSelectedItem(); - } else { - selectedItem = mAdapter.getItem(position); - } - if (selectedItem != null) { - setText(mFilter.convertResultToString(selectedItem)); - pushTextToInputConnection(); - } - } - mPopup.dismiss(); - } - - public void setIsAutoFillProfileSet(boolean isAutoFillProfileSet) { - mIsAutoFillProfileSet = isAutoFillProfileSet; - } - - private void pushTextToInputConnection() { - Editable oldText = mInputConnection.getEditable(); - mInputConnection.setSelection(0, oldText.length()); - mInputConnection.replaceSelection(mText); - mInputConnection.setSelection(mText.length(), mText.length()); - } - - @Override - public void onFilterComplete(int count) { - ensurePopup(); - boolean showDropDown = (count > 0) && - (mInputConnection.getIsAutoFillable() || mText.length() > 0); - if (showDropDown) { - if (!mPopup.isShowing()) { - // Make sure the list does not obscure the IME when shown for the first time. - mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED); - } - mPopup.show(); - mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); - } else { - mPopup.dismiss(); - } - } - - @Override - public void onDismiss() { - mWebView.getWebView().removeView(mAnchor); - } - - private void ensurePopup() { - if (mPopup == null) { - mPopup = new ListPopupWindow(mWebView.getContext()); - mAnchor = new AnchorView(mWebView.getContext()); - mWebView.getWebView().addView(mAnchor); - mPopup.setOnItemClickListener(this); - mPopup.setAnchorView(mAnchor); - mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); - } else if (mWebView.getWebView().indexOfChild(mAnchor) < 0) { - mWebView.getWebView().addView(mAnchor); - } - } -} - diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java deleted file mode 100644 index 6955d14..0000000 --- a/core/java/android/webkit/BrowserFrame.java +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright (C) 2006 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 android.webkit; - -import android.app.ActivityManager; -import android.content.ComponentCallbacks; -import android.content.Context; -import android.content.res.AssetManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.graphics.Bitmap; -import android.net.ParseException; -import android.net.Uri; -import android.net.WebAddress; -import android.net.http.ErrorStrings; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.os.Handler; -import android.os.Message; -import android.util.Log; -import android.util.TypedValue; -import android.view.Surface; -import android.view.ViewRootImpl; -import android.view.WindowManager; - -import junit.framework.Assert; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.WeakReference; -import java.net.URLEncoder; -import java.security.PrivateKey; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import com.android.org.conscrypt.OpenSSLKey; -import com.android.org.conscrypt.OpenSSLKeyHolder; - -class BrowserFrame extends Handler { - - private static final String LOGTAG = "webkit"; - - /** - * Cap the number of LoadListeners that will be instantiated, so - * we don't blow the GREF count. Attempting to queue more than - * this many requests will prompt an error() callback on the - * request's LoadListener - */ - private final static int MAX_OUTSTANDING_REQUESTS = 300; - - private final CallbackProxy mCallbackProxy; - private final WebSettingsClassic mSettings; - private final Context mContext; - private final WebViewDatabaseClassic mDatabase; - private final WebViewCore mWebViewCore; - /* package */ boolean mLoadInitFromJava; - private int mLoadType; - private boolean mFirstLayoutDone = true; - private boolean mCommitted = true; - // Flag for blocking messages. This is used during destroy() so - // that if the UI thread posts any messages after the message - // queue has been cleared,they are ignored. - private boolean mBlockMessages = false; - private int mOrientation = -1; - - // Is this frame the main frame? - private boolean mIsMainFrame; - - // Javascript interface object - private class JSObject { - Object object; - boolean requireAnnotation; - - public JSObject(Object object, boolean requireAnnotation) { - this.object = object; - this.requireAnnotation = requireAnnotation; - } - } - - // Attached Javascript interfaces - private Map<String, JSObject> mJavaScriptObjects; - private Set<Object> mRemovedJavaScriptObjects; - - // Key store handler when Chromium HTTP stack is used. - private KeyStoreHandler mKeyStoreHandler = null; - - // message ids - // a message posted when a frame loading is completed - static final int FRAME_COMPLETED = 1001; - // orientation change message - static final int ORIENTATION_CHANGED = 1002; - // a message posted when the user decides the policy - static final int POLICY_FUNCTION = 1003; - - // Note: need to keep these in sync with FrameLoaderTypes.h in native - static final int FRAME_LOADTYPE_STANDARD = 0; - static final int FRAME_LOADTYPE_BACK = 1; - static final int FRAME_LOADTYPE_FORWARD = 2; - static final int FRAME_LOADTYPE_INDEXEDBACKFORWARD = 3; - static final int FRAME_LOADTYPE_RELOAD = 4; - static final int FRAME_LOADTYPE_RELOADALLOWINGSTALEDATA = 5; - static final int FRAME_LOADTYPE_SAME = 6; - static final int FRAME_LOADTYPE_REDIRECT = 7; - static final int FRAME_LOADTYPE_REPLACE = 8; - - // A progress threshold to switch from history Picture to live Picture - private static final int TRANSITION_SWITCH_THRESHOLD = 75; - - // This is a field accessed by native code as well as package classes. - /*package*/ int mNativeFrame; - - // Static instance of a JWebCoreJavaBridge to handle timer and cookie - // requests from WebCore. - static JWebCoreJavaBridge sJavaBridge; - - private static class ConfigCallback implements ComponentCallbacks { - private final ArrayList<WeakReference<Handler>> mHandlers = - new ArrayList<WeakReference<Handler>>(); - private final WindowManager mWindowManager; - - ConfigCallback(WindowManager wm) { - mWindowManager = wm; - } - - public synchronized void addHandler(Handler h) { - // No need to ever remove a Handler. If the BrowserFrame is - // destroyed, it will be collected and the WeakReference set to - // null. If it happens to still be around during a configuration - // change, the message will be ignored. - mHandlers.add(new WeakReference<Handler>(h)); - } - - public void onConfigurationChanged(Configuration newConfig) { - if (mHandlers.size() == 0) { - return; - } - int orientation = - mWindowManager.getDefaultDisplay().getOrientation(); - switch (orientation) { - case Surface.ROTATION_90: - orientation = 90; - break; - case Surface.ROTATION_180: - orientation = 180; - break; - case Surface.ROTATION_270: - orientation = -90; - break; - case Surface.ROTATION_0: - orientation = 0; - break; - default: - break; - } - synchronized (this) { - // Create a list of handlers to remove. Go ahead and make it - // the same size to avoid resizing. - ArrayList<WeakReference> handlersToRemove = - new ArrayList<WeakReference>(mHandlers.size()); - for (WeakReference<Handler> wh : mHandlers) { - Handler h = wh.get(); - if (h != null) { - h.sendMessage(h.obtainMessage(ORIENTATION_CHANGED, - orientation, 0)); - } else { - handlersToRemove.add(wh); - } - } - // Now remove all the null references. - for (WeakReference weak : handlersToRemove) { - mHandlers.remove(weak); - } - } - } - - public void onLowMemory() {} - } - static ConfigCallback sConfigCallback; - - /** - * Create a new BrowserFrame to be used in an application. - * @param context An application context to use when retrieving assets. - * @param w A WebViewCore used as the view for this frame. - * @param proxy A CallbackProxy for posting messages to the UI thread and - * querying a client for information. - * @param settings A WebSettings object that holds all settings. - * XXX: Called by WebCore thread. - */ - public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy, - WebSettingsClassic settings, Map<String, Object> javascriptInterfaces) { - - Context appContext = context.getApplicationContext(); - - // Create a global JWebCoreJavaBridge to handle timers and - // cookies in the WebCore thread. - if (sJavaBridge == null) { - sJavaBridge = new JWebCoreJavaBridge(); - // set WebCore native cache size - ActivityManager am = (ActivityManager) context - .getSystemService(Context.ACTIVITY_SERVICE); - if (am.getMemoryClass() > 16) { - sJavaBridge.setCacheSize(8 * 1024 * 1024); - } else { - sJavaBridge.setCacheSize(4 * 1024 * 1024); - } - // create CookieSyncManager with current Context - CookieSyncManager.createInstance(appContext); - // create PluginManager with current Context - PluginManager.getInstance(appContext); - } - - if (sConfigCallback == null) { - sConfigCallback = new ConfigCallback( - (WindowManager) appContext.getSystemService( - Context.WINDOW_SERVICE)); - ViewRootImpl.addConfigCallback(sConfigCallback); - } - sConfigCallback.addHandler(this); - - mJavaScriptObjects = new HashMap<String, JSObject>(); - addJavaScriptObjects(javascriptInterfaces); - mRemovedJavaScriptObjects = new HashSet<Object>(); - - mSettings = settings; - mContext = context; - mCallbackProxy = proxy; - mDatabase = WebViewDatabaseClassic.getInstance(appContext); - mWebViewCore = w; - - AssetManager am = context.getAssets(); - nativeCreateFrame(w, am, proxy.getBackForwardList()); - - if (DebugFlags.BROWSER_FRAME) { - Log.v(LOGTAG, "BrowserFrame constructor: this=" + this); - } - } - - /** - * Load a url from the network or the filesystem into the main frame. - * Following the same behaviour as Safari, javascript: URLs are not passed - * to the main frame, instead they are evaluated immediately. - * @param url The url to load. - * @param extraHeaders The extra headers sent with this url. This should not - * include the common headers like "user-agent". If it does, it - * will be replaced by the intrinsic value of the WebView. - */ - public void loadUrl(String url, Map<String, String> extraHeaders) { - mLoadInitFromJava = true; - if (URLUtil.isJavaScriptUrl(url)) { - // strip off the scheme and evaluate the string - stringByEvaluatingJavaScriptFromString( - url.substring("javascript:".length())); - } else { - nativeLoadUrl(url, extraHeaders); - } - mLoadInitFromJava = false; - } - - /** - * Load a url with "POST" method from the network into the main frame. - * @param url The url to load. - * @param data The data for POST request. - */ - public void postUrl(String url, byte[] data) { - mLoadInitFromJava = true; - nativePostUrl(url, data); - mLoadInitFromJava = false; - } - - /** - * Load the content as if it was loaded by the provided base URL. The - * historyUrl is used as the history entry for the load data. - * - * @param baseUrl Base URL used to resolve relative paths in the content - * @param data Content to render in the browser - * @param mimeType Mimetype of the data being passed in - * @param encoding Character set encoding of the provided data. - * @param historyUrl URL to use as the history entry. - */ - public void loadData(String baseUrl, String data, String mimeType, - String encoding, String historyUrl) { - mLoadInitFromJava = true; - if (historyUrl == null || historyUrl.length() == 0) { - historyUrl = "about:blank"; - } - if (data == null) { - data = ""; - } - - // Setup defaults for missing values. These defaults where taken from - // WebKit's WebFrame.mm - if (baseUrl == null || baseUrl.length() == 0) { - baseUrl = "about:blank"; - } - if (mimeType == null || mimeType.length() == 0) { - mimeType = "text/html"; - } - nativeLoadData(baseUrl, data, mimeType, encoding, historyUrl); - mLoadInitFromJava = false; - } - - /** - * Saves the contents of the frame as a web archive. - * - * @param basename The filename where the archive should be placed. - * @param autoname If false, takes filename to be a file. If true, filename - * is assumed to be a directory in which a filename will be - * chosen according to the url of the current page. - */ - /* package */ String saveWebArchive(String basename, boolean autoname) { - return nativeSaveWebArchive(basename, autoname); - } - - /** - * Go back or forward the number of steps given. - * @param steps A negative or positive number indicating the direction - * and number of steps to move. - */ - public void goBackOrForward(int steps) { - mLoadInitFromJava = true; - nativeGoBackOrForward(steps); - mLoadInitFromJava = false; - } - - /** - * native callback - * Report an error to an activity. - * @param errorCode The HTTP error code. - * @param description Optional human-readable description. If no description - * is given, we'll use a standard localized error message. - * @param failingUrl The URL that was being loaded when the error occurred. - * TODO: Report all errors including resource errors but include some kind - * of domain identifier. Change errorCode to an enum for a cleaner - * interface. - */ - private void reportError(int errorCode, String description, String failingUrl) { - // As this is called for the main resource and loading will be stopped - // after, reset the state variables. - resetLoadingStates(); - if (description == null || description.isEmpty()) { - description = ErrorStrings.getString(errorCode, mContext); - } - mCallbackProxy.onReceivedError(errorCode, description, failingUrl); - } - - private void resetLoadingStates() { - mCommitted = true; - mFirstLayoutDone = true; - } - - /* package */boolean committed() { - return mCommitted; - } - - /* package */boolean firstLayoutDone() { - return mFirstLayoutDone; - } - - /* package */int loadType() { - return mLoadType; - } - - /* package */void didFirstLayout() { - if (!mFirstLayoutDone) { - mFirstLayoutDone = true; - // ensure {@link WebViewCore#webkitDraw} is called as we were - // blocking the update in {@link #loadStarted} - mWebViewCore.contentDraw(); - } - } - - /** - * native callback - * Indicates the beginning of a new load. - * This method will be called once for the main frame. - */ - private void loadStarted(String url, Bitmap favicon, int loadType, - boolean isMainFrame) { - mIsMainFrame = isMainFrame; - - if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { - mLoadType = loadType; - - if (isMainFrame) { - // Call onPageStarted for main frames. - mCallbackProxy.onPageStarted(url, favicon); - // as didFirstLayout() is only called for the main frame, reset - // mFirstLayoutDone only for the main frames - mFirstLayoutDone = false; - mCommitted = false; - // remove pending draw to block update until mFirstLayoutDone is - // set to true in didFirstLayout() - mWebViewCore.clearContent(); - mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); - } - } - } - - @SuppressWarnings("unused") - private void saveFormData(HashMap<String, String> data) { - if (mSettings.getSaveFormData()) { - final WebHistoryItem h = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - if (h != null) { - String url = WebTextView.urlForAutoCompleteData(h.getUrl()); - if (url != null) { - mDatabase.setFormData(url, data); - } - } - } - } - - @SuppressWarnings("unused") - private boolean shouldSaveFormData() { - if (mSettings.getSaveFormData()) { - final WebHistoryItem h = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - return h != null && h.getUrl() != null; - } - return false; - } - - /** - * native callback - * Indicates the WebKit has committed to the new load - */ - private void transitionToCommitted(int loadType, boolean isMainFrame) { - // loadType is not used yet - if (isMainFrame) { - mCommitted = true; - mWebViewCore.getWebViewClassic().mViewManager.postResetStateAll(); - } - } - - /** - * native callback - * <p> - * Indicates the end of a new load. - * This method will be called once for the main frame. - */ - private void loadFinished(String url, int loadType, boolean isMainFrame) { - // mIsMainFrame and isMainFrame are better be equal!!! - - if (isMainFrame || loadType == FRAME_LOADTYPE_STANDARD) { - if (isMainFrame) { - resetLoadingStates(); - mCallbackProxy.switchOutDrawHistory(); - mCallbackProxy.onPageFinished(url); - } - } - } - - /** - * Destroy all native components of the BrowserFrame. - */ - public void destroy() { - nativeDestroyFrame(); - mBlockMessages = true; - removeCallbacksAndMessages(null); - } - - /** - * Handle messages posted to us. - * @param msg The message to handle. - */ - @Override - public void handleMessage(Message msg) { - if (mBlockMessages) { - return; - } - switch (msg.what) { - case FRAME_COMPLETED: { - if (mSettings.getSavePassword() && hasPasswordField()) { - WebHistoryItem item = mCallbackProxy.getBackForwardList() - .getCurrentItem(); - if (item != null) { - WebAddress uri = new WebAddress(item.getUrl()); - String schemePlusHost = uri.getScheme() + uri.getHost(); - String[] up = - WebViewDatabaseClassic.getInstance(mContext) - .getUsernamePassword(schemePlusHost); - if (up != null && up[0] != null) { - setUsernamePassword(up[0], up[1]); - } - } - } - break; - } - - case POLICY_FUNCTION: { - nativeCallPolicyFunction(msg.arg1, msg.arg2); - break; - } - - case ORIENTATION_CHANGED: { - if (mOrientation != msg.arg1) { - mOrientation = msg.arg1; - nativeOrientationChanged(msg.arg1); - } - break; - } - - default: - break; - } - } - - /** - * Punch-through for WebCore to set the document - * title. Inform the Activity of the new title. - * @param title The new title of the document. - */ - private void setTitle(String title) { - // FIXME: The activity must call getTitle (a native method) to get the - // title. We should try and cache the title if we can also keep it in - // sync with the document. - mCallbackProxy.onReceivedTitle(title); - } - - /** - * Retrieves the render tree of this frame and puts it as the object for - * the message and sends the message. - * @param callback the message to use to send the render tree - */ - public void externalRepresentation(Message callback) { - callback.obj = externalRepresentation();; - callback.sendToTarget(); - } - - /** - * Return the render tree as a string - */ - private native String externalRepresentation(); - - /** - * Retrieves the visual text of the frames, puts it as the object for - * the message and sends the message. - * @param callback the message to use to send the visual text - */ - public void documentAsText(Message callback) { - StringBuilder text = new StringBuilder(); - if (callback.arg1 != 0) { - // Dump top frame as text. - text.append(documentAsText()); - } - if (callback.arg2 != 0) { - // Dump child frames as text. - text.append(childFramesAsText()); - } - callback.obj = text.toString(); - callback.sendToTarget(); - } - - /** - * Return the text drawn on the screen as a string - */ - private native String documentAsText(); - - /** - * Return the text drawn on the child frames as a string - */ - private native String childFramesAsText(); - - /* - * This method is called by WebCore to inform the frame that - * the Javascript window object has been cleared. - * We should re-attach any attached js interfaces. - */ - private void windowObjectCleared(int nativeFramePointer) { - Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); - while (iter.hasNext()) { - String interfaceName = iter.next(); - JSObject jsobject = mJavaScriptObjects.get(interfaceName); - if (jsobject != null && jsobject.object != null) { - nativeAddJavascriptInterface(nativeFramePointer, - jsobject.object, interfaceName, jsobject.requireAnnotation); - } - } - mRemovedJavaScriptObjects.clear(); - } - - /* - * Add javascript objects to the internal list of objects. The default behavior - * is to allow access to inherited methods (no annotation needed). This is only - * used when js objects are passed through a constructor (via a hidden constructor). - * - * @TODO change the default behavior to be compatible with the public addjavascriptinterface - */ - private void addJavaScriptObjects(Map<String, Object> javascriptInterfaces) { - - // TODO in a separate CL provide logic to enable annotations for API level JB_MR1 and above. - if (javascriptInterfaces == null) return; - Iterator<String> iter = javascriptInterfaces.keySet().iterator(); - while (iter.hasNext()) { - String interfaceName = iter.next(); - Object object = javascriptInterfaces.get(interfaceName); - if (object != null) { - mJavaScriptObjects.put(interfaceName, new JSObject(object, false)); - } - } - } - - /** - * This method is called by WebCore to check whether application - * wants to hijack url loading - */ - public boolean handleUrl(String url) { - if (mLoadInitFromJava == true) { - return false; - } - if (mCallbackProxy.shouldOverrideUrlLoading(url)) { - // if the url is hijacked, reset the state of the BrowserFrame - didFirstLayout(); - return true; - } else { - return false; - } - } - - public void addJavascriptInterface(Object obj, String interfaceName, - boolean requireAnnotation) { - assert obj != null; - removeJavascriptInterface(interfaceName); - mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation)); - } - - public void removeJavascriptInterface(String interfaceName) { - // We keep a reference to the removed object because the native side holds only a weak - // reference and we need to allow the object to continue to be used until the page has been - // navigated. - if (mJavaScriptObjects.containsKey(interfaceName)) { - mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName)); - } - } - - /** - * Called by JNI. Given a URI, find the associated file and return its size - * @param uri A String representing the URI of the desired file. - * @return int The size of the given file. - */ - private int getFileSize(String uri) { - int size = 0; - try { - InputStream stream = mContext.getContentResolver() - .openInputStream(Uri.parse(uri)); - size = stream.available(); - stream.close(); - } catch (Exception e) {} - return size; - } - - /** - * Called by JNI. Given a URI, a buffer, and an offset into the buffer, - * copy the resource into buffer. - * @param uri A String representing the URI of the desired file. - * @param buffer The byte array to copy the data into. - * @param offset The offet into buffer to place the data. - * @param expectedSize The size that the buffer has allocated for this file. - * @return int The size of the given file, or zero if it fails. - */ - private int getFile(String uri, byte[] buffer, int offset, - int expectedSize) { - int size = 0; - try { - InputStream stream = mContext.getContentResolver() - .openInputStream(Uri.parse(uri)); - size = stream.available(); - if (size <= expectedSize && buffer != null - && buffer.length - offset >= size) { - stream.read(buffer, offset, size); - } else { - size = 0; - } - stream.close(); - } catch (java.io.FileNotFoundException e) { - Log.e(LOGTAG, "FileNotFoundException:" + e); - size = 0; - } catch (java.io.IOException e2) { - Log.e(LOGTAG, "IOException: " + e2); - size = 0; - } - return size; - } - - /** - * Get the InputStream for an Android resource - * There are three different kinds of android resources: - * - file:///android_res - * - file:///android_asset - * - content:// - * @param url The url to load. - * @return An InputStream to the android resource - */ - private InputStream inputStreamForAndroidResource(String url) { - final String ANDROID_ASSET = URLUtil.ASSET_BASE; - final String ANDROID_RESOURCE = URLUtil.RESOURCE_BASE; - final String ANDROID_CONTENT = URLUtil.CONTENT_BASE; - - if (url.startsWith(ANDROID_RESOURCE)) { - url = url.replaceFirst(ANDROID_RESOURCE, ""); - if (url == null || url.length() == 0) { - Log.e(LOGTAG, "url has length 0 " + url); - return null; - } - int slash = url.indexOf('/'); - int dot = url.indexOf('.', slash); - if (slash == -1 || dot == -1) { - Log.e(LOGTAG, "Incorrect res path: " + url); - return null; - } - String subClassName = url.substring(0, slash); - String fieldName = url.substring(slash + 1, dot); - String errorMsg = null; - try { - final Class<?> d = mContext.getApplicationContext() - .getClassLoader().loadClass( - mContext.getPackageName() + ".R$" - + subClassName); - final java.lang.reflect.Field field = d.getField(fieldName); - final int id = field.getInt(null); - TypedValue value = new TypedValue(); - mContext.getResources().getValue(id, value, true); - if (value.type == TypedValue.TYPE_STRING) { - return mContext.getAssets().openNonAsset( - value.assetCookie, value.string.toString(), - AssetManager.ACCESS_STREAMING); - } else { - // Old stack only supports TYPE_STRING for res files - Log.e(LOGTAG, "not of type string: " + url); - return null; - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return null; - } - } else if (url.startsWith(ANDROID_ASSET)) { - String assetUrl = url.replaceFirst(ANDROID_ASSET, ""); - try { - AssetManager assets = mContext.getAssets(); - Uri uri = Uri.parse(assetUrl); - return assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING); - } catch (IOException e) { - return null; - } catch (Exception e) { - Log.w(LOGTAG, "Problem loading url: " + url, e); - return null; - } - } else if (mSettings.getAllowContentAccess() && - url.startsWith(ANDROID_CONTENT)) { - try { - // Strip off MIME type. If we don't do this, we can fail to - // load Gmail attachments, because the URL being loaded doesn't - // exactly match the URL we have permission to read. - int mimeIndex = url.lastIndexOf('?'); - if (mimeIndex != -1) { - url = url.substring(0, mimeIndex); - } - Uri uri = Uri.parse(url); - return mContext.getContentResolver().openInputStream(uri); - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return null; - } - } else { - return null; - } - } - - /** - * If this looks like a POST request (form submission) containing a username - * and password, give the user the option of saving them. Will either do - * nothing, or block until the UI interaction is complete. - * - * Called directly by WebKit. - * - * @param postData The data about to be sent as the body of a POST request. - * @param username The username entered by the user (sniffed from the DOM). - * @param password The password entered by the user (sniffed from the DOM). - */ - private void maybeSavePassword( - byte[] postData, String username, String password) { - if (postData == null - || username == null || username.isEmpty() - || password == null || password.isEmpty()) { - return; // No password to save. - } - - if (!mSettings.getSavePassword()) { - return; // User doesn't want to save passwords. - } - - try { - if (DebugFlags.BROWSER_FRAME) { - Assert.assertNotNull(mCallbackProxy.getBackForwardList() - .getCurrentItem()); - } - WebAddress uri = new WebAddress(mCallbackProxy - .getBackForwardList().getCurrentItem().getUrl()); - String schemePlusHost = uri.getScheme() + uri.getHost(); - // Check to see if the username & password appear in - // the post data (there could be another form on the - // page and that was posted instead. - String postString = new String(postData); - if (postString.contains(URLEncoder.encode(username)) && - postString.contains(URLEncoder.encode(password))) { - String[] saved = mDatabase.getUsernamePassword( - schemePlusHost); - if (saved != null) { - // null username implies that user has chosen not to - // save password - if (saved[0] != null) { - // non-null username implies that user has - // chosen to save password, so update the - // recorded password - mDatabase.setUsernamePassword(schemePlusHost, username, password); - } - } else { - // CallbackProxy will handle creating the resume - // message - mCallbackProxy.onSavePassword(schemePlusHost, username, - password, null); - } - } - } catch (ParseException ex) { - // if it is bad uri, don't save its password - } - } - - // Called by jni from the chrome network stack. - private WebResourceResponse shouldInterceptRequest(String url) { - InputStream androidResource = inputStreamForAndroidResource(url); - if (androidResource != null) { - return new WebResourceResponse(null, null, androidResource); - } - - // Note that we check this after looking for an android_asset or - // android_res URL, as we allow those even if file access is disabled. - if (!mSettings.getAllowFileAccess() && url.startsWith("file://")) { - return new WebResourceResponse(null, null, null); - } - - WebResourceResponse response = mCallbackProxy.shouldInterceptRequest(url); - if (response == null && "browser:incognito".equals(url)) { - try { - Resources res = mContext.getResources(); - InputStream ins = res.openRawResource( - com.android.internal.R.raw.incognito_mode_start_page); - response = new WebResourceResponse("text/html", "utf8", ins); - } catch (NotFoundException ex) { - // This shouldn't happen, but try and gracefully handle it jic - Log.w(LOGTAG, "Failed opening raw.incognito_mode_start_page", ex); - } - } - return response; - } - - /** - * Set the progress for the browser activity. Called by native code. - * Uses a delay so it does not happen too often. - * @param newProgress An int between zero and one hundred representing - * the current progress percentage of loading the page. - */ - private void setProgress(int newProgress) { - mCallbackProxy.onProgressChanged(newProgress); - if (newProgress == 100) { - sendMessageDelayed(obtainMessage(FRAME_COMPLETED), 100); - } - // FIXME: Need to figure out a better way to switch out of the history - // drawing mode. Maybe we can somehow compare the history picture with - // the current picture, and switch when it contains more content. - if (mFirstLayoutDone && newProgress > TRANSITION_SWITCH_THRESHOLD) { - mCallbackProxy.switchOutDrawHistory(); - } - } - - /** - * Send the icon to the activity for display. - * @param icon A Bitmap representing a page's favicon. - */ - private void didReceiveIcon(Bitmap icon) { - mCallbackProxy.onReceivedIcon(icon); - } - - // Called by JNI when an apple-touch-icon attribute was found. - private void didReceiveTouchIconUrl(String url, boolean precomposed) { - mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); - } - - /** - * Request a new window from the client. - * @return The BrowserFrame object stored in the new WebView. - */ - private BrowserFrame createWindow(boolean dialog, boolean userGesture) { - return mCallbackProxy.createWindow(dialog, userGesture); - } - - /** - * Try to focus this WebView. - */ - private void requestFocus() { - mCallbackProxy.onRequestFocus(); - } - - /** - * Close this frame and window. - */ - private void closeWindow(WebViewCore w) { - mCallbackProxy.onCloseWindow(w.getWebViewClassic()); - } - - // XXX: Must match PolicyAction in FrameLoaderTypes.h in webcore - static final int POLICY_USE = 0; - static final int POLICY_IGNORE = 2; - - private void decidePolicyForFormResubmission(int policyFunction) { - Message dontResend = obtainMessage(POLICY_FUNCTION, policyFunction, - POLICY_IGNORE); - Message resend = obtainMessage(POLICY_FUNCTION, policyFunction, - POLICY_USE); - mCallbackProxy.onFormResubmission(dontResend, resend); - } - - /** - * Tell the activity to update its global history. - */ - private void updateVisitedHistory(String url, boolean isReload) { - mCallbackProxy.doUpdateVisitedHistory(url, isReload); - } - - /** - * Get the CallbackProxy for sending messages to the UI thread. - */ - /* package */ CallbackProxy getCallbackProxy() { - return mCallbackProxy; - } - - /** - * Returns the User Agent used by this frame - */ - String getUserAgentString() { - return mSettings.getUserAgentString(); - } - - // These ids need to be in sync with enum rawResId in PlatformBridge.h - private static final int NODOMAIN = 1; - private static final int LOADERROR = 2; - /* package */ static final int DRAWABLEDIR = 3; - private static final int FILE_UPLOAD_LABEL = 4; - private static final int RESET_LABEL = 5; - private static final int SUBMIT_LABEL = 6; - private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7; - - private String getRawResFilename(int id) { - return getRawResFilename(id, mContext); - } - /* package */ static String getRawResFilename(int id, Context context) { - int resid; - switch (id) { - case NODOMAIN: - resid = com.android.internal.R.raw.nodomain; - break; - - case LOADERROR: - resid = com.android.internal.R.raw.loaderror; - break; - - case DRAWABLEDIR: - // use one known resource to find the drawable directory - resid = com.android.internal.R.drawable.btn_check_off; - break; - - case FILE_UPLOAD_LABEL: - return context.getResources().getString( - com.android.internal.R.string.upload_file); - - case RESET_LABEL: - return context.getResources().getString( - com.android.internal.R.string.reset); - - case SUBMIT_LABEL: - return context.getResources().getString( - com.android.internal.R.string.submit); - - case FILE_UPLOAD_NO_FILE_CHOSEN: - return context.getResources().getString( - com.android.internal.R.string.no_file_chosen); - - default: - Log.e(LOGTAG, "getRawResFilename got incompatible resource ID"); - return ""; - } - TypedValue value = new TypedValue(); - context.getResources().getValue(resid, value, true); - if (id == DRAWABLEDIR) { - String path = value.string.toString(); - int index = path.lastIndexOf('/'); - if (index < 0) { - Log.e(LOGTAG, "Can't find drawable directory."); - return ""; - } - return path.substring(0, index + 1); - } - return value.string.toString(); - } - - private float density() { - return WebViewCore.getFixedDisplayDensity(mContext); - } - - /** - * Called by JNI when the native HTTP stack gets an authentication request. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeAuthenticationProceed(int, String, String)} or - * {@link #nativeAuthenticationCancel(int)}. - * - * We don't care what thread the callback is invoked on. All threading is - * handled on the C++ side, because the WebKit thread may be blocked on a - * synchronous call and unable to pump our MessageQueue. - */ - private void didReceiveAuthenticationChallenge( - final int handle, String host, String realm, final boolean useCachedCredentials, - final boolean suppressDialog) { - - HttpAuthHandler handler = new HttpAuthHandler() { - - @Override - public boolean useHttpAuthUsernamePassword() { - return useCachedCredentials; - } - - @Override - public void proceed(String username, String password) { - nativeAuthenticationProceed(handle, username, password); - } - - @Override - public void cancel() { - nativeAuthenticationCancel(handle); - } - - @Override - public boolean suppressDialog() { - return suppressDialog; - } - }; - mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm); - } - - /** - * Called by JNI when the Chromium HTTP stack gets an invalid certificate chain. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeSslCertErrorProceed(int)} or - * {@link #nativeSslCertErrorCancel(int, int)}. - */ - private void reportSslCertError(final int handle, final int certError, byte certDER[], - String url) { - final SslError sslError; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(certDER)); - SslCertificate sslCert = new SslCertificate(cert); - sslError = SslError.SslErrorFromChromiumErrorCode(certError, sslCert, url); - } catch (Exception e) { - // Can't get the certificate, not much to do. - Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); - nativeSslCertErrorCancel(handle, certError); - return; - } - - if (SslCertLookupTable.getInstance().isAllowed(sslError)) { - nativeSslCertErrorProceed(handle); - mCallbackProxy.onProceededAfterSslError(sslError); - return; - } - - SslErrorHandler handler = new SslErrorHandler() { - @Override - public void proceed() { - SslCertLookupTable.getInstance().setIsAllowed(sslError); - post(new Runnable() { - public void run() { - nativeSslCertErrorProceed(handle); - } - }); - } - @Override - public void cancel() { - post(new Runnable() { - public void run() { - nativeSslCertErrorCancel(handle, certError); - } - }); - } - }; - mCallbackProxy.onReceivedSslError(handler, sslError); - } - - /** - * Called by JNI when the native HTTPS stack gets a client - * certificate request. - * - * We delegate the request to CallbackProxy, and route its response to - * {@link #nativeSslClientCert(int, X509Certificate)}. - */ - private void requestClientCert(int handle, String hostAndPort) { - SslClientCertLookupTable table = SslClientCertLookupTable.getInstance(); - if (table.IsAllowed(hostAndPort)) { - // previously allowed - PrivateKey pkey = table.PrivateKey(hostAndPort); - if (pkey instanceof OpenSSLKeyHolder) { - OpenSSLKey sslKey = ((OpenSSLKeyHolder) pkey).getOpenSSLKey(); - nativeSslClientCert(handle, - sslKey.getPkeyContext(), - table.CertificateChain(hostAndPort)); - } else { - nativeSslClientCert(handle, - pkey.getEncoded(), - table.CertificateChain(hostAndPort)); - } - } else if (table.IsDenied(hostAndPort)) { - // previously denied - nativeSslClientCert(handle, 0, null); - } else { - // previously ignored or new - mCallbackProxy.onReceivedClientCertRequest( - new ClientCertRequestHandler(this, handle, hostAndPort, table), hostAndPort); - } - } - - /** - * Called by JNI when the native HTTP stack needs to download a file. - * - * We delegate the request to CallbackProxy, which owns the current app's - * DownloadListener. - */ - private void downloadStart(String url, String userAgent, - String contentDisposition, String mimeType, String referer, long contentLength) { - // This will only work if the url ends with the filename - if (mimeType.isEmpty()) { - try { - String extension = url.substring(url.lastIndexOf('.') + 1); - mimeType = libcore.net.MimeUtils.guessMimeTypeFromExtension(extension); - // MimeUtils might return null, not sure if downloadmanager is happy with that - if (mimeType == null) - mimeType = ""; - } catch(IndexOutOfBoundsException exception) { - // mimeType string end with a '.', not much to do - } - } - mimeType = MimeTypeMap.getSingleton().remapGenericMimeType( - mimeType, url, contentDisposition); - - if (CertTool.getCertType(mimeType) != null) { - mKeyStoreHandler = new KeyStoreHandler(mimeType); - } else { - mCallbackProxy.onDownloadStart(url, userAgent, - contentDisposition, mimeType, referer, contentLength); - } - } - - /** - * Called by JNI for Chrome HTTP stack when the Java side needs to access the data. - */ - private void didReceiveData(byte data[], int size) { - if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size); - } - - private void didFinishLoading() { - if (mKeyStoreHandler != null) { - mKeyStoreHandler.installCert(mContext); - mKeyStoreHandler = null; - } - } - - /** - * Called by JNI when we recieve a certificate for the page's main resource. - * Used by the Chromium HTTP stack only. - */ - private void setCertificate(byte cert_der[]) { - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(cert_der)); - mCallbackProxy.onReceivedCertificate(new SslCertificate(cert)); - } catch (Exception e) { - // Can't get the certificate, not much to do. - Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling"); - return; - } - } - - /** - * Called by JNI when processing the X-Auto-Login header. - */ - private void autoLogin(String realm, String account, String args) { - mCallbackProxy.onReceivedLoginRequest(realm, account, args); - } - - //========================================================================== - // native functions - //========================================================================== - - /** - * Create a new native frame for a given WebView - * @param w A WebView that the frame draws into. - * @param am AssetManager to use to get assets. - * @param list The native side will add and remove items from this list as - * the native list changes. - */ - private native void nativeCreateFrame(WebViewCore w, AssetManager am, - WebBackForwardList list); - - /** - * Destroy the native frame. - */ - public native void nativeDestroyFrame(); - - private native void nativeCallPolicyFunction(int policyFunction, - int decision); - - /** - * Reload the current main frame. - */ - public native void reload(boolean allowStale); - - /** - * Go back or forward the number of steps given. - * @param steps A negative or positive number indicating the direction - * and number of steps to move. - */ - private native void nativeGoBackOrForward(int steps); - - /** - * stringByEvaluatingJavaScriptFromString will execute the - * JS passed in in the context of this browser frame. - * @param script A javascript string to execute - * - * @return string result of execution or null - */ - public native String stringByEvaluatingJavaScriptFromString(String script); - - /** - * Add a javascript interface to the main frame. - */ - private native void nativeAddJavascriptInterface(int nativeFramePointer, - Object obj, String interfaceName, boolean requireAnnotation); - - public native void clearCache(); - - /** - * Returns false if the url is bad. - */ - private native void nativeLoadUrl(String url, Map<String, String> headers); - - private native void nativePostUrl(String url, byte[] postData); - - private native void nativeLoadData(String baseUrl, String data, - String mimeType, String encoding, String historyUrl); - - /** - * Stop loading the current page. - */ - public void stopLoading() { - if (mIsMainFrame) { - resetLoadingStates(); - } - nativeStopLoading(); - } - - private native void nativeStopLoading(); - - /** - * Return true if the document has images. - */ - public native boolean documentHasImages(); - - /** - * @return TRUE if there is a password field in the current frame - */ - private native boolean hasPasswordField(); - - /** - * Get username and password in the current frame. If found, String[0] is - * username and String[1] is password. Otherwise return NULL. - * @return String[] - */ - private native String[] getUsernamePassword(); - - /** - * Set username and password to the proper fields in the current frame - * @param username - * @param password - */ - private native void setUsernamePassword(String username, String password); - - private native String nativeSaveWebArchive(String basename, boolean autoname); - - private native void nativeOrientationChanged(int orientation); - - private native void nativeAuthenticationProceed(int handle, String username, String password); - private native void nativeAuthenticationCancel(int handle); - - private native void nativeSslCertErrorProceed(int handle); - private native void nativeSslCertErrorCancel(int handle, int certError); - - native void nativeSslClientCert(int handle, - long ctx, - byte[][] asn1DerEncodedCertificateChain); - - native void nativeSslClientCert(int handle, - byte[] pkey, - byte[][] asn1DerEncodedCertificateChain); - - /** - * Returns true when the contents of the frame is an RTL or vertical-rl - * page. This is used for determining whether a frame should be initially - * scrolled right-most as opposed to left-most. - * @return true when the frame should be initially scrolled right-most - * based on the text direction and writing mode. - */ - /* package */ boolean getShouldStartScrolledRight() { - return nativeGetShouldStartScrolledRight(mNativeFrame); - } - - private native boolean nativeGetShouldStartScrolledRight(int nativeBrowserFrame); -} diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java deleted file mode 100644 index 334526b..0000000 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2006 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 android.webkit; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.util.LinkedList; -import java.util.ListIterator; - -/** Utility class optimized for accumulating bytes, and then spitting - them back out. It does not optimize for returning the result in a - single array, though this is supported in the API. It is fastest - if the retrieval can be done via iterating through chunks. -*/ -class ByteArrayBuilder { - - private static final int DEFAULT_CAPACITY = 8192; - - // Global pool of chunks to be used by other ByteArrayBuilders. - private static final LinkedList<SoftReference<Chunk>> sPool = - new LinkedList<SoftReference<Chunk>>(); - // Reference queue for processing gc'd entries. - private static final ReferenceQueue<Chunk> sQueue = - new ReferenceQueue<Chunk>(); - - private LinkedList<Chunk> mChunks; - - public ByteArrayBuilder() { - mChunks = new LinkedList<Chunk>(); - } - - public synchronized void append(byte[] array, int offset, int length) { - while (length > 0) { - Chunk c = null; - if (mChunks.isEmpty()) { - c = obtainChunk(length); - mChunks.addLast(c); - } else { - c = mChunks.getLast(); - if (c.mLength == c.mArray.length) { - c = obtainChunk(length); - mChunks.addLast(c); - } - } - int amount = Math.min(length, c.mArray.length - c.mLength); - System.arraycopy(array, offset, c.mArray, c.mLength, amount); - c.mLength += amount; - length -= amount; - offset += amount; - } - } - - /** - * The fastest way to retrieve the data is to iterate through the - * chunks. This returns the first chunk. Note: this pulls the - * chunk out of the queue. The caller must call Chunk.release() to - * dispose of it. - */ - public synchronized Chunk getFirstChunk() { - if (mChunks.isEmpty()) return null; - return mChunks.removeFirst(); - } - - public synchronized boolean isEmpty() { - return mChunks.isEmpty(); - } - - public synchronized int getByteSize() { - int total = 0; - ListIterator<Chunk> it = mChunks.listIterator(0); - while (it.hasNext()) { - Chunk c = it.next(); - total += c.mLength; - } - return total; - } - - public synchronized void clear() { - Chunk c = getFirstChunk(); - while (c != null) { - c.release(); - c = getFirstChunk(); - } - } - - // Must be called with lock held on sPool. - private void processPoolLocked() { - while (true) { - SoftReference<Chunk> entry = (SoftReference<Chunk>) sQueue.poll(); - if (entry == null) { - break; - } - sPool.remove(entry); - } - } - - private Chunk obtainChunk(int length) { - // Correct a small length. - if (length < DEFAULT_CAPACITY) { - length = DEFAULT_CAPACITY; - } - synchronized (sPool) { - // Process any queued references and remove them from the pool. - processPoolLocked(); - if (!sPool.isEmpty()) { - Chunk c = sPool.removeFirst().get(); - // The first item may have been queued after processPoolLocked - // so check for null. - if (c != null) { - return c; - } - } - return new Chunk(length); - } - } - - public static class Chunk { - public byte[] mArray; - public int mLength; - - public Chunk(int length) { - mArray = new byte[length]; - mLength = 0; - } - - /** - * Release the chunk and make it available for reuse. - */ - public void release() { - mLength = 0; - synchronized (sPool) { - // Add the chunk back to the pool as a SoftReference so it can - // be gc'd if needed. - sPool.offer(new SoftReference<Chunk>(this, sQueue)); - sPool.notifyAll(); - } - } - - } -} diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java deleted file mode 100644 index bbd3f2b..0000000 --- a/core/java/android/webkit/CacheManager.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2006 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 android.webkit; - -import android.content.Context; -import android.net.http.Headers; -import android.util.Log; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Map; - - -/** - * Manages the HTTP cache used by an application's {@link WebView} instances. - * @deprecated Access to the HTTP cache will be removed in a future release. - * @hide Since {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ -// The class CacheManager provides the persistent cache of content that is -// received over the network. The component handles parsing of HTTP headers and -// utilizes the relevant cache headers to determine if the content should be -// stored and if so, how long it is valid for. Network requests are provided to -// this component and if they can not be resolved by the cache, the HTTP headers -// are attached, as appropriate, to the request for revalidation of content. The -// class also manages the cache size. -// -// CacheManager may only be used if your activity contains a WebView. -@Deprecated -public final class CacheManager { - /** - * Represents a resource stored in the HTTP cache. Instances of this class - * can be obtained by calling - * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}. - * - * @deprecated Access to the HTTP cache will be removed in a future release. - */ - @Deprecated - public static class CacheResult { - // these fields are saved to the database - int httpStatusCode; - long contentLength; - long expires; - String expiresString; - String localPath; - String lastModified; - String etag; - String mimeType; - String location; - String encoding; - String contentdisposition; - String crossDomain; - - // these fields are NOT saved to the database - InputStream inStream; - OutputStream outStream; - File outFile; - - /** - * Gets the status code of this cache entry. - * - * @return the status code of this cache entry - */ - public int getHttpStatusCode() { - return httpStatusCode; - } - - /** - * Gets the content length of this cache entry. - * - * @return the content length of this cache entry - */ - public long getContentLength() { - return contentLength; - } - - /** - * Gets the path of the file used to store the content of this cache - * entry, relative to the base directory of the cache. See - * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}. - * - * @return the path of the file used to store this cache entry - */ - public String getLocalPath() { - return localPath; - } - - /** - * Gets the expiry date of this cache entry, expressed in milliseconds - * since midnight, January 1, 1970 UTC. - * - * @return the expiry date of this cache entry - */ - public long getExpires() { - return expires; - } - - /** - * Gets the expiry date of this cache entry, expressed as a string. - * - * @return the expiry date of this cache entry - * - */ - public String getExpiresString() { - return expiresString; - } - - /** - * Gets the date at which this cache entry was last modified, expressed - * as a string. - * - * @return the date at which this cache entry was last modified - */ - public String getLastModified() { - return lastModified; - } - - /** - * Gets the entity tag of this cache entry. - * - * @return the entity tag of this cache entry - */ - public String getETag() { - return etag; - } - - /** - * Gets the MIME type of this cache entry. - * - * @return the MIME type of this cache entry - */ - public String getMimeType() { - return mimeType; - } - - /** - * Gets the value of the HTTP 'Location' header with which this cache - * entry was received. - * - * @return the HTTP 'Location' header for this cache entry - */ - public String getLocation() { - return location; - } - - /** - * Gets the encoding of this cache entry. - * - * @return the encoding of this cache entry - */ - public String getEncoding() { - return encoding; - } - - /** - * Gets the value of the HTTP 'Content-Disposition' header with which - * this cache entry was received. - * - * @return the HTTP 'Content-Disposition' header for this cache entry - * - */ - public String getContentDisposition() { - return contentdisposition; - } - - /** - * Gets the input stream to the content of this cache entry, to allow - * content to be read. See - * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}. - * - * @return an input stream to the content of this cache entry - */ - public InputStream getInputStream() { - return inStream; - } - - /** - * Gets an output stream to the content of this cache entry, to allow - * content to be written. See - * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}. - * - * @return an output stream to the content of this cache entry - */ - // Note that this is always null for objects returned by getCacheFile()! - public OutputStream getOutputStream() { - return outStream; - } - - - /** - * Sets an input stream to the content of this cache entry. - * - * @param stream an input stream to the content of this cache entry - */ - public void setInputStream(InputStream stream) { - this.inStream = stream; - } - - /** - * Sets the encoding of this cache entry. - * - * @param encoding the encoding of this cache entry - */ - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - /** - * @hide - */ - public void setContentLength(long contentLength) { - this.contentLength = contentLength; - } - } - - /** - * Gets the base directory in which the files used to store the contents of - * cache entries are placed. See - * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}. - * - * @return the base directory of the cache - * @deprecated This method no longer has any effect and always returns null. - */ - @Deprecated - public static File getCacheFileBaseDir() { - return null; - } - - /** - * Gets whether the HTTP cache is disabled. - * - * @return true if the HTTP cache is disabled - * @deprecated This method no longer has any effect and always returns false. - */ - @Deprecated - public static boolean cacheDisabled() { - return false; - } - - /** - * Starts a cache transaction. Returns true if this is the only running - * transaction. Otherwise, this transaction is nested inside currently - * running transactions and false is returned. - * - * @return true if this is the only running transaction - * @deprecated This method no longer has any effect and always returns false. - */ - @Deprecated - public static boolean startCacheTransaction() { - return false; - } - - /** - * Ends the innermost cache transaction and returns whether this was the - * only running transaction. - * - * @return true if this was the only running transaction - * @deprecated This method no longer has any effect and always returns false. - */ - @Deprecated - public static boolean endCacheTransaction() { - return false; - } - - /** - * Gets the cache entry for the specified URL, or null if none is found. - * If a non-null value is provided for the HTTP headers map, and the cache - * entry needs validation, appropriate headers will be added to the map. - * The input stream of the CacheEntry object should be closed by the caller - * when access to the underlying file is no longer required. - * - * @param url the URL for which a cache entry is requested - * @param headers a map from HTTP header name to value, to be populated - * for the returned cache entry - * @return the cache entry for the specified URL - * @deprecated This method no longer has any effect and always returns null. - */ - @Deprecated - public static CacheResult getCacheFile(String url, - Map<String, String> headers) { - return null; - } - - /** - * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes - * the cache entry's output stream. - * - * @param url the URL for which the cache entry should be added - * @param cacheResult the cache entry to add - * @deprecated Access to the HTTP cache will be removed in a future release. - */ - @Deprecated - public static void saveCacheFile(String url, CacheResult cacheResult) { - saveCacheFile(url, 0, cacheResult); - } - - static void saveCacheFile(String url, long postIdentifier, - CacheResult cacheRet) { - try { - cacheRet.outStream.close(); - } catch (IOException e) { - return; - } - - // This method is exposed in the public API but the API provides no - // way to obtain a new CacheResult object with a non-null output - // stream ... - // - CacheResult objects returned by getCacheFile() have a null - // output stream. - // - new CacheResult objects have a null output stream and no - // setter is provided. - // Since this method throws a null pointer exception in this case, - // it is effectively useless from the point of view of the public - // API. - // - // With the Chromium HTTP stack we continue to throw the same - // exception for 'backwards compatibility' with the Android HTTP - // stack. - // - // This method is not used from within this package, and for public API - // use, we should already have thrown an exception above. - assert false; - } -} diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java deleted file mode 100644 index 7707392..0000000 --- a/core/java/android/webkit/CallbackProxy.java +++ /dev/null @@ -1,1452 +0,0 @@ -/* - * Copyright (C) 2007 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 android.webkit; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.provider.Browser; -import android.util.Log; -import android.view.KeyEvent; -import com.android.internal.R; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This class is a proxy class for handling WebCore -> UI thread messaging. All - * the callback functions are called from the WebCore thread and messages are - * posted to the UI thread for the actual client callback. - */ -/* - * This class is created in the UI thread so its handler and any private classes - * that extend Handler will operate in the UI thread. - */ -class CallbackProxy extends Handler { - // Logging tag - static final String LOGTAG = "WebViewCallback"; - // Enables API callback tracing - private static final boolean TRACE = DebugFlags.TRACE_CALLBACK; - // Instance of WebViewClient that is the client callback. - private volatile WebViewClient mWebViewClient; - // Instance of WebChromeClient for handling all chrome functions. - private volatile WebChromeClient mWebChromeClient; - // Instance of WebViewClassic for handling UI requests. - private final WebViewClassic mWebView; - // Client registered callback listener for download events - private volatile DownloadListener mDownloadListener; - // Keep track of multiple progress updates. - private boolean mProgressUpdatePending; - // Keep track of the last progress amount. - // Start with 100 to indicate it is not in load for the empty page. - private volatile int mLatestProgress = 100; - // Back/Forward list - private final WebBackForwardListClassic mBackForwardList; - // Back/Forward list client - private volatile WebBackForwardListClient mWebBackForwardListClient; - // Used to call startActivity during url override. - private final Context mContext; - // block messages flag for destroy - private boolean mBlockMessages; - - // Message IDs - private static final int PAGE_STARTED = 100; - private static final int RECEIVED_ICON = 101; - private static final int RECEIVED_TITLE = 102; - private static final int OVERRIDE_URL = 103; - private static final int AUTH_REQUEST = 104; - private static final int SSL_ERROR = 105; - private static final int PROGRESS = 106; - private static final int UPDATE_VISITED = 107; - private static final int LOAD_RESOURCE = 108; - private static final int CREATE_WINDOW = 109; - private static final int CLOSE_WINDOW = 110; - private static final int SAVE_PASSWORD = 111; - private static final int JS_DIALOG = 112; - private static final int ASYNC_KEYEVENTS = 116; - private static final int DOWNLOAD_FILE = 118; - private static final int REPORT_ERROR = 119; - private static final int RESEND_POST_DATA = 120; - private static final int PAGE_FINISHED = 121; - private static final int REQUEST_FOCUS = 122; - private static final int SCALE_CHANGED = 123; - private static final int RECEIVED_CERTIFICATE = 124; - private static final int SWITCH_OUT_HISTORY = 125; - private static final int EXCEEDED_DATABASE_QUOTA = 126; - private static final int REACHED_APPCACHE_MAXSIZE = 127; - private static final int JS_TIMEOUT = 128; - private static final int ADD_MESSAGE_TO_CONSOLE = 129; - private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT = 130; - private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131; - private static final int RECEIVED_TOUCH_ICON_URL = 132; - private static final int GET_VISITED_HISTORY = 133; - private static final int OPEN_FILE_CHOOSER = 134; - private static final int ADD_HISTORY_ITEM = 135; - private static final int HISTORY_INDEX_CHANGED = 136; - private static final int AUTH_CREDENTIALS = 137; - private static final int AUTO_LOGIN = 140; - private static final int CLIENT_CERT_REQUEST = 141; - private static final int PROCEEDED_AFTER_SSL_ERROR = 144; - - // Message triggered by the client to resume execution - private static final int NOTIFY = 200; - - // Result transportation object for returning results across thread - // boundaries. - private static class ResultTransport<E> { - // Private result object - private E mResult; - - public ResultTransport(E defaultResult) { - mResult = defaultResult; - } - - public synchronized void setResult(E result) { - mResult = result; - } - - public synchronized E getResult() { - return mResult; - } - } - - private class JsResultReceiver implements JsResult.ResultReceiver { - // This prevents a user from interacting with the result before WebCore is - // ready to handle it. - private boolean mReady; - // Tells us if the user tried to confirm or cancel the result before WebCore - // is ready. - private boolean mTriedToNotifyBeforeReady; - - public JsPromptResult mJsResult = new JsPromptResult(this); - - final void setReady() { - mReady = true; - if (mTriedToNotifyBeforeReady) { - notifyCallbackProxy(); - } - } - - /* Wake up the WebCore thread. */ - @Override - public void onJsResultComplete(JsResult result) { - if (mReady) { - notifyCallbackProxy(); - } else { - mTriedToNotifyBeforeReady = true; - } - } - - private void notifyCallbackProxy() { - synchronized (CallbackProxy.this) { - CallbackProxy.this.notify(); - } - } -} - - /** - * Construct a new CallbackProxy. - */ - public CallbackProxy(Context context, WebViewClassic w) { - // Used to start a default activity. - mContext = context; - mWebView = w; - mBackForwardList = new WebBackForwardListClassic(this); - } - - protected synchronized void blockMessages() { - mBlockMessages = true; - } - - protected synchronized boolean messagesBlocked() { - return mBlockMessages; - } - - protected void shutdown() { - removeCallbacksAndMessages(null); - setWebViewClient(null); - setWebChromeClient(null); - } - - /** - * Set the WebViewClient. - * @param client An implementation of WebViewClient. - */ - public void setWebViewClient(WebViewClient client) { - mWebViewClient = client; - } - - /** - * Get the WebViewClient. - * @return the current WebViewClient instance. - */ - public WebViewClient getWebViewClient() { - return mWebViewClient; - } - - /** - * Set the WebChromeClient. - * @param client An implementation of WebChromeClient. - */ - public void setWebChromeClient(WebChromeClient client) { - mWebChromeClient = client; - } - - /** - * Get the WebChromeClient. - * @return the current WebChromeClient instance. - */ - public WebChromeClient getWebChromeClient() { - return mWebChromeClient; - } - - /** - * Set the client DownloadListener. - * @param client An implementation of DownloadListener. - */ - public void setDownloadListener(DownloadListener client) { - mDownloadListener = client; - } - - /** - * Get the Back/Forward list to return to the user or to update the cached - * history list. - */ - public WebBackForwardListClassic getBackForwardList() { - return mBackForwardList; - } - - void setWebBackForwardListClient(WebBackForwardListClient client) { - mWebBackForwardListClient = client; - } - - WebBackForwardListClient getWebBackForwardListClient() { - return mWebBackForwardListClient; - } - - /** - * Called by the UI side. Calling overrideUrlLoading from the WebCore - * side will post a message to call this method. - */ - public boolean uiOverrideUrlLoading(String overrideUrl) { - if (overrideUrl == null || overrideUrl.length() == 0) { - return false; - } - boolean override = false; - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "shouldOverrideUrlLoading=" + overrideUrl); - override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(), - overrideUrl); - } else { - Intent intent = new Intent(Intent.ACTION_VIEW, - Uri.parse(overrideUrl)); - intent.addCategory(Intent.CATEGORY_BROWSABLE); - // If another application is running a WebView and launches the - // Browser through this Intent, we want to reuse the same window if - // possible. - intent.putExtra(Browser.EXTRA_APPLICATION_ID, - mContext.getPackageName()); - try { - mContext.startActivity(intent); - override = true; - } catch (ActivityNotFoundException ex) { - // If no application can handle the URL, assume that the - // browser can handle it. - } - } - return override; - } - - /** - * Called by UI side. - */ - public boolean uiOverrideKeyEvent(KeyEvent event) { - if (mWebViewClient != null) { - return mWebViewClient.shouldOverrideKeyEvent(mWebView.getWebView(), event); - } - return false; - } - - @Override - public void handleMessage(Message msg) { - // We don't have to do synchronization because this function operates - // in the UI thread. The WebViewClient and WebChromeClient functions - // that check for a non-null callback are ok because java ensures atomic - // 32-bit reads and writes. - if (messagesBlocked()) { - synchronized (this) { - notify(); - } - return; - } - switch (msg.what) { - case PAGE_STARTED: - String startedUrl = msg.getData().getString("url"); - mWebView.onPageStarted(startedUrl); - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "onPageStarted=" + startedUrl); - mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl, - (Bitmap) msg.obj); - } - break; - - case PAGE_FINISHED: - String finishedUrl = (String) msg.obj; - mWebView.onPageFinished(finishedUrl); - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "onPageFinished=" + finishedUrl); - mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl); - } - break; - - case RECEIVED_ICON: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onReceivedIcon"); - mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj); - } - break; - - case RECEIVED_TOUCH_ICON_URL: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onReceivedTouchIconUrl"); - mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(), - (String) msg.obj, msg.arg1 == 1); - } - break; - - case RECEIVED_TITLE: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onReceivedTitle"); - mWebChromeClient.onReceivedTitle(mWebView.getWebView(), - (String) msg.obj); - } - break; - - case REPORT_ERROR: - if (mWebViewClient != null) { - int reasonCode = msg.arg1; - final String description = msg.getData().getString("description"); - final String failUrl = msg.getData().getString("failingUrl"); - if (TRACE) Log.d(LOGTAG, "onReceivedError=" + failUrl); - mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode, - description, failUrl); - } - break; - - case RESEND_POST_DATA: - Message resend = - (Message) msg.getData().getParcelable("resend"); - Message dontResend = - (Message) msg.getData().getParcelable("dontResend"); - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "onFormResubmission"); - mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend, - resend); - } else { - dontResend.sendToTarget(); - } - break; - - case OVERRIDE_URL: - String overrideUrl = msg.getData().getString("url"); - boolean override = uiOverrideUrlLoading(overrideUrl); - ResultTransport<Boolean> result = - (ResultTransport<Boolean>) msg.obj; - synchronized (this) { - result.setResult(override); - notify(); - } - break; - - case AUTH_REQUEST: - if (mWebViewClient != null) { - HttpAuthHandler handler = (HttpAuthHandler) msg.obj; - String host = msg.getData().getString("host"); - String realm = msg.getData().getString("realm"); - if (TRACE) Log.d(LOGTAG, "onReceivedHttpAuthRequest"); - mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler, - host, realm); - } - break; - - case SSL_ERROR: - if (mWebViewClient != null) { - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - if (TRACE) Log.d(LOGTAG, "onReceivedSslError"); - mWebViewClient.onReceivedSslError(mWebView.getWebView(), - (SslErrorHandler) map.get("handler"), - (SslError) map.get("error")); - } - break; - - case PROCEEDED_AFTER_SSL_ERROR: - if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) { - if (TRACE) Log.d(LOGTAG, "onProceededAfterSslError"); - ((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError( - mWebView.getWebView(), - (SslError) msg.obj); - } - break; - - case CLIENT_CERT_REQUEST: - if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) { - if (TRACE) Log.d(LOGTAG, "onReceivedClientCertRequest"); - HashMap<String, Object> map = (HashMap<String, Object>) msg.obj; - ((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest( - mWebView.getWebView(), - (ClientCertRequestHandler) map.get("handler"), - (String) map.get("host_and_port")); - } - break; - - case PROGRESS: - // Synchronize to ensure mLatestProgress is not modified after - // setProgress is called and before mProgressUpdatePending is - // changed. - synchronized (this) { - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onProgressChanged=" + mLatestProgress); - mWebChromeClient.onProgressChanged(mWebView.getWebView(), - mLatestProgress); - } - mProgressUpdatePending = false; - } - break; - - case UPDATE_VISITED: - if (mWebViewClient != null) { - String url = (String) msg.obj; - if (TRACE) Log.d(LOGTAG, "doUpdateVisitedHistory=" + url); - mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(), - url, msg.arg1 != 0); - } - break; - - case LOAD_RESOURCE: - if (mWebViewClient != null) { - String url = (String) msg.obj; - if (TRACE) Log.d(LOGTAG, "onLoadResource=" + url); - mWebViewClient.onLoadResource(mWebView.getWebView(), url); - } - break; - - case DOWNLOAD_FILE: - if (mDownloadListener != null) { - String url = msg.getData().getString("url"); - String userAgent = msg.getData().getString("userAgent"); - String contentDisposition = - msg.getData().getString("contentDisposition"); - String mimetype = msg.getData().getString("mimetype"); - String referer = msg.getData().getString("referer"); - Long contentLength = msg.getData().getLong("contentLength"); - - if (TRACE) Log.d(LOGTAG, "onDownloadStart"); - if (mDownloadListener instanceof BrowserDownloadListener) { - ((BrowserDownloadListener) mDownloadListener).onDownloadStart(url, - userAgent, contentDisposition, mimetype, referer, contentLength); - } else { - mDownloadListener.onDownloadStart(url, userAgent, - contentDisposition, mimetype, contentLength); - } - } - break; - - case CREATE_WINDOW: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onCreateWindow"); - if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(), - msg.arg1 == 1, msg.arg2 == 1, - (Message) msg.obj)) { - synchronized (this) { - notify(); - } - } - mWebView.dismissZoomControl(); - } - break; - - case REQUEST_FOCUS: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onRequestFocus"); - mWebChromeClient.onRequestFocus(mWebView.getWebView()); - } - break; - - case CLOSE_WINDOW: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onCloseWindow"); - mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView()); - } - break; - - case SAVE_PASSWORD: - Bundle bundle = msg.getData(); - String schemePlusHost = bundle.getString("host"); - String username = bundle.getString("username"); - String password = bundle.getString("password"); - // If the client returned false it means that the notify message - // will not be sent and we should notify WebCore ourselves. - if (!mWebView.onSavePassword(schemePlusHost, username, password, - (Message) msg.obj)) { - synchronized (this) { - notify(); - } - } - break; - - case ASYNC_KEYEVENTS: - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "onUnhandledKeyEvent"); - mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(), - (KeyEvent) msg.obj); - } - break; - - case EXCEEDED_DATABASE_QUOTA: - if (mWebChromeClient != null) { - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - String databaseIdentifier = - (String) map.get("databaseIdentifier"); - String url = (String) map.get("url"); - long quota = - ((Long) map.get("quota")).longValue(); - long totalQuota = - ((Long) map.get("totalQuota")).longValue(); - long estimatedDatabaseSize = - ((Long) map.get("estimatedDatabaseSize")).longValue(); - WebStorage.QuotaUpdater quotaUpdater = - (WebStorage.QuotaUpdater) map.get("quotaUpdater"); - - if (TRACE) Log.d(LOGTAG, "onExceededDatabaseQuota"); - mWebChromeClient.onExceededDatabaseQuota(url, - databaseIdentifier, quota, estimatedDatabaseSize, - totalQuota, quotaUpdater); - } - break; - - case REACHED_APPCACHE_MAXSIZE: - if (mWebChromeClient != null) { - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - long requiredStorage = - ((Long) map.get("requiredStorage")).longValue(); - long quota = - ((Long) map.get("quota")).longValue(); - WebStorage.QuotaUpdater quotaUpdater = - (WebStorage.QuotaUpdater) map.get("quotaUpdater"); - - if (TRACE) Log.d(LOGTAG, "onReachedMaxAppCacheSize"); - mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage, - quota, quotaUpdater); - } - break; - - case GEOLOCATION_PERMISSIONS_SHOW_PROMPT: - if (mWebChromeClient != null) { - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - String origin = (String) map.get("origin"); - GeolocationPermissions.Callback callback = - (GeolocationPermissions.Callback) - map.get("callback"); - if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsShowPrompt"); - mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, - callback); - } - break; - - case GEOLOCATION_PERMISSIONS_HIDE_PROMPT: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "onGeolocationPermissionsHidePrompt"); - mWebChromeClient.onGeolocationPermissionsHidePrompt(); - } - break; - - case JS_DIALOG: - if (mWebChromeClient != null) { - final JsResultReceiver receiver = (JsResultReceiver) msg.obj; - JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg); - if (TRACE) Log.d(LOGTAG, "onJsAlert"); - if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) { - helper.showDialog(mContext); - } - receiver.setReady(); - } - break; - - case JS_TIMEOUT: - if(mWebChromeClient != null) { - final JsResultReceiver receiver = (JsResultReceiver) msg.obj; - final JsResult res = receiver.mJsResult; - if (TRACE) Log.d(LOGTAG, "onJsTimeout"); - if (mWebChromeClient.onJsTimeout()) { - res.confirm(); - } else { - res.cancel(); - } - receiver.setReady(); - } - break; - - case RECEIVED_CERTIFICATE: - mWebView.setCertificate((SslCertificate) msg.obj); - break; - - case NOTIFY: - synchronized (this) { - notify(); - } - break; - - case SCALE_CHANGED: - if (mWebViewClient != null) { - if (TRACE) Log.d(LOGTAG, "onScaleChanged"); - mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData() - .getFloat("old"), msg.getData().getFloat("new")); - } - break; - - case SWITCH_OUT_HISTORY: - mWebView.switchOutDrawHistory(); - break; - - case ADD_MESSAGE_TO_CONSOLE: - if (mWebChromeClient == null) { - break; - } - String message = msg.getData().getString("message"); - String sourceID = msg.getData().getString("sourceID"); - int lineNumber = msg.getData().getInt("lineNumber"); - int msgLevel = msg.getData().getInt("msgLevel"); - int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length; - // Sanity bounds check as we'll index an array with msgLevel - if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) { - msgLevel = 0; - } - - ConsoleMessage.MessageLevel messageLevel = - ConsoleMessage.MessageLevel.values()[msgLevel]; - - if (TRACE) Log.d(LOGTAG, "onConsoleMessage"); - if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID, - lineNumber, messageLevel))) { - // If false was returned the user did not provide their own console function so - // we should output some default messages to the system log. - String logTag = "Web Console"; - String logMessage = message + " at " + sourceID + ":" + lineNumber; - - switch (messageLevel) { - case TIP: - Log.v(logTag, logMessage); - break; - case LOG: - Log.i(logTag, logMessage); - break; - case WARNING: - Log.w(logTag, logMessage); - break; - case ERROR: - Log.e(logTag, logMessage); - break; - case DEBUG: - Log.d(logTag, logMessage); - break; - } - } - - break; - - case GET_VISITED_HISTORY: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "getVisitedHistory"); - mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj); - } - break; - - case OPEN_FILE_CHOOSER: - if (mWebChromeClient != null) { - if (TRACE) Log.d(LOGTAG, "openFileChooser"); - UploadFileMessageData data = (UploadFileMessageData)msg.obj; - mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(), - data.getCapture()); - } - break; - - case ADD_HISTORY_ITEM: - if (mWebBackForwardListClient != null) { - if (TRACE) Log.d(LOGTAG, "onNewHistoryItem"); - mWebBackForwardListClient.onNewHistoryItem( - (WebHistoryItem) msg.obj); - } - break; - - case HISTORY_INDEX_CHANGED: - if (mWebBackForwardListClient != null) { - mWebBackForwardListClient.onIndexChanged( - (WebHistoryItem) msg.obj, msg.arg1); - } - break; - case AUTH_CREDENTIALS: { - String host = msg.getData().getString("host"); - String realm = msg.getData().getString("realm"); - username = msg.getData().getString("username"); - password = msg.getData().getString("password"); - mWebView.setHttpAuthUsernamePassword( - host, realm, username, password); - break; - } - case AUTO_LOGIN: { - if (mWebViewClient != null) { - String realm = msg.getData().getString("realm"); - String account = msg.getData().getString("account"); - String args = msg.getData().getString("args"); - if (TRACE) Log.d(LOGTAG, "onReceivedLoginRequest"); - mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm, - account, args); - } - break; - } - } - } - - /** - * Return the latest progress. - */ - public int getProgress() { - return mLatestProgress; - } - - /** - * Called by WebCore side to switch out of history Picture drawing mode - */ - void switchOutDrawHistory() { - sendMessage(obtainMessage(SWITCH_OUT_HISTORY)); - } - - //-------------------------------------------------------------------------- - // WebViewClient functions. - // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so - // it is not necessary to include it here. - //-------------------------------------------------------------------------- - - // Performance probe - private static final boolean PERF_PROBE = false; - private long mWebCoreThreadTime; - private long mWebCoreIdleTime; - - /* - * If PERF_PROBE is true, this block needs to be added to MessageQueue.java. - * startWait() and finishWait() should be called before and after wait(). - - private WaitCallback mWaitCallback = null; - public static interface WaitCallback { - void startWait(); - void finishWait(); - } - public final void setWaitCallback(WaitCallback callback) { - mWaitCallback = callback; - } - */ - - // un-comment this block if PERF_PROBE is true - /* - private IdleCallback mIdleCallback = new IdleCallback(); - - private final class IdleCallback implements MessageQueue.WaitCallback { - private long mStartTime = 0; - - public void finishWait() { - mWebCoreIdleTime += SystemClock.uptimeMillis() - mStartTime; - } - - public void startWait() { - mStartTime = SystemClock.uptimeMillis(); - } - } - */ - - public void onPageStarted(String url, Bitmap favicon) { - // We need to send the message even if no WebViewClient is set, because we need to call - // WebView.onPageStarted(). - - // Performance probe - if (PERF_PROBE) { - mWebCoreThreadTime = SystemClock.currentThreadTimeMillis(); - mWebCoreIdleTime = 0; - // un-comment this if PERF_PROBE is true -// Looper.myQueue().setWaitCallback(mIdleCallback); - } - Message msg = obtainMessage(PAGE_STARTED); - msg.obj = favicon; - msg.getData().putString("url", url); - sendMessage(msg); - } - - public void onPageFinished(String url) { - // Performance probe - if (PERF_PROBE) { - // un-comment this if PERF_PROBE is true -// Looper.myQueue().setWaitCallback(null); - Log.d("WebCore", "WebCore thread used " + - (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime) - + " ms and idled " + mWebCoreIdleTime + " ms"); - } - Message msg = obtainMessage(PAGE_FINISHED, url); - sendMessage(msg); - } - - // Because this method is public and because CallbackProxy is mistakenly - // party of the public classes, we cannot remove this method. - public void onTooManyRedirects(Message cancelMsg, Message continueMsg) { - // deprecated. - } - - public void onReceivedError(int errorCode, String description, - String failingUrl) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - return; - } - - Message msg = obtainMessage(REPORT_ERROR); - msg.arg1 = errorCode; - msg.getData().putString("description", description); - msg.getData().putString("failingUrl", failingUrl); - sendMessage(msg); - } - - public void onFormResubmission(Message dontResend, - Message resend) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - dontResend.sendToTarget(); - return; - } - - Message msg = obtainMessage(RESEND_POST_DATA); - Bundle bundle = msg.getData(); - bundle.putParcelable("resend", resend); - bundle.putParcelable("dontResend", dontResend); - sendMessage(msg); - } - - /** - * Called by the WebCore side - */ - public boolean shouldOverrideUrlLoading(String url) { - // We have a default behavior if no client exists so always send the - // message. - ResultTransport<Boolean> res = new ResultTransport<Boolean>(false); - Message msg = obtainMessage(OVERRIDE_URL); - msg.getData().putString("url", url); - msg.obj = res; - sendMessageToUiThreadSync(msg); - return res.getResult().booleanValue(); - } - - public void onReceivedHttpAuthRequest(HttpAuthHandler handler, - String hostName, String realmName) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - handler.cancel(); - return; - } - Message msg = obtainMessage(AUTH_REQUEST, handler); - msg.getData().putString("host", hostName); - msg.getData().putString("realm", realmName); - sendMessage(msg); - } - - public void onReceivedSslError(SslErrorHandler handler, SslError error) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - handler.cancel(); - return; - } - Message msg = obtainMessage(SSL_ERROR); - HashMap<String, Object> map = new HashMap(); - map.put("handler", handler); - map.put("error", error); - msg.obj = map; - sendMessage(msg); - } - - public void onProceededAfterSslError(SslError error) { - if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) { - return; - } - Message msg = obtainMessage(PROCEEDED_AFTER_SSL_ERROR); - msg.obj = error; - sendMessage(msg); - } - - public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) { - handler.cancel(); - return; - } - Message msg = obtainMessage(CLIENT_CERT_REQUEST); - HashMap<String, Object> map = new HashMap(); - map.put("handler", handler); - map.put("host_and_port", host_and_port); - msg.obj = map; - sendMessage(msg); - } - - public void onReceivedCertificate(SslCertificate certificate) { - // here, certificate can be null (if the site is not secure) - sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate)); - } - - public void doUpdateVisitedHistory(String url, boolean isReload) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - return; - } - sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url)); - } - - WebResourceResponse shouldInterceptRequest(String url) { - if (mWebViewClient == null) { - return null; - } - // Note: This method does _not_ send a message. - if (TRACE) Log.d(LOGTAG, "shouldInterceptRequest=" + url); - WebResourceResponse r = - mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url); - if (r == null) { - sendMessage(obtainMessage(LOAD_RESOURCE, url)); - } - return r; - } - - public void onUnhandledKeyEvent(KeyEvent event) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - return; - } - sendMessage(obtainMessage(ASYNC_KEYEVENTS, event)); - } - - public void onScaleChanged(float oldScale, float newScale) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - return; - } - Message msg = obtainMessage(SCALE_CHANGED); - Bundle bundle = msg.getData(); - bundle.putFloat("old", oldScale); - bundle.putFloat("new", newScale); - sendMessage(msg); - } - - void onReceivedLoginRequest(String realm, String account, String args) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - return; - } - Message msg = obtainMessage(AUTO_LOGIN); - Bundle bundle = msg.getData(); - bundle.putString("realm", realm); - bundle.putString("account", account); - bundle.putString("args", args); - sendMessage(msg); - } - - //-------------------------------------------------------------------------- - // DownloadListener functions. - //-------------------------------------------------------------------------- - - /** - * Starts a download if a download listener has been registered, otherwise - * return false. - */ - public boolean onDownloadStart(String url, String userAgent, - String contentDisposition, String mimetype, String referer, - long contentLength) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mDownloadListener == null) { - // Cancel the download if there is no browser client. - return false; - } - - Message msg = obtainMessage(DOWNLOAD_FILE); - Bundle bundle = msg.getData(); - bundle.putString("url", url); - bundle.putString("userAgent", userAgent); - bundle.putString("mimetype", mimetype); - bundle.putString("referer", referer); - bundle.putLong("contentLength", contentLength); - bundle.putString("contentDisposition", contentDisposition); - sendMessage(msg); - return true; - } - - - //-------------------------------------------------------------------------- - // WebView specific functions that do not interact with a client. These - // functions just need to operate within the UI thread. - //-------------------------------------------------------------------------- - - public boolean onSavePassword(String schemePlusHost, String username, - String password, Message resumeMsg) { - // resumeMsg should be null at this point because we want to create it - // within the CallbackProxy. - if (DebugFlags.CALLBACK_PROXY) { - junit.framework.Assert.assertNull(resumeMsg); - } - resumeMsg = obtainMessage(NOTIFY); - - Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg); - Bundle bundle = msg.getData(); - bundle.putString("host", schemePlusHost); - bundle.putString("username", username); - bundle.putString("password", password); - sendMessageToUiThreadSync(msg); - // Doesn't matter here - return false; - } - - public void onReceivedHttpAuthCredentials(String host, String realm, - String username, String password) { - Message msg = obtainMessage(AUTH_CREDENTIALS); - msg.getData().putString("host", host); - msg.getData().putString("realm", realm); - msg.getData().putString("username", username); - msg.getData().putString("password", password); - sendMessage(msg); - } - - //-------------------------------------------------------------------------- - // WebChromeClient methods - //-------------------------------------------------------------------------- - - public void onProgressChanged(int newProgress) { - // Synchronize so that mLatestProgress is up-to-date. - synchronized (this) { - // update mLatestProgress even mWebChromeClient is null as - // WebView.getProgress() needs it - if (mLatestProgress == newProgress) { - return; - } - mLatestProgress = newProgress; - if (mWebChromeClient == null) { - return; - } - if (!mProgressUpdatePending) { - sendEmptyMessage(PROGRESS); - mProgressUpdatePending = true; - } - } - } - - public BrowserFrame createWindow(boolean dialog, boolean userGesture) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return null; - } - - WebView.WebViewTransport transport = - mWebView.getWebView().new WebViewTransport(); - final Message msg = obtainMessage(NOTIFY); - msg.obj = transport; - sendMessageToUiThreadSync(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0, - userGesture ? 1 : 0, msg)); - WebViewClassic w = WebViewClassic.fromWebView(transport.getWebView()); - if (w != null) { - WebViewCore core = w.getWebViewCore(); - // If WebView.destroy() has been called, core may be null. Skip - // initialization in that case and return null. - if (core != null) { - core.initializeSubwindow(); - return core.getBrowserFrame(); - } - } - return null; - } - - public void onRequestFocus() { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - - sendEmptyMessage(REQUEST_FOCUS); - } - - public void onCloseWindow(WebViewClassic window) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - sendMessage(obtainMessage(CLOSE_WINDOW, window)); - } - - public void onReceivedIcon(Bitmap icon) { - // The current item might be null if the icon was already stored in the - // database and this is a new WebView. - WebHistoryItemClassic i = mBackForwardList.getCurrentItem(); - if (i != null) { - i.setFavicon(icon); - } - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - sendMessage(obtainMessage(RECEIVED_ICON, icon)); - } - - /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) { - // We should have a current item but we do not want to crash so check - // for null. - WebHistoryItemClassic i = mBackForwardList.getCurrentItem(); - if (i != null) { - i.setTouchIconUrl(url, precomposed); - } - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, - precomposed ? 1 : 0, 0, url)); - } - - public void onReceivedTitle(String title) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - sendMessage(obtainMessage(RECEIVED_TITLE, title)); - } - - public void onJsAlert(String url, String message) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return; - } - JsResultReceiver result = new JsResultReceiver(); - Message alert = obtainMessage(JS_DIALOG, result); - alert.getData().putString("message", message); - alert.getData().putString("url", url); - alert.getData().putInt("type", JsDialogHelper.ALERT); - sendMessageToUiThreadSync(alert); - } - - public boolean onJsConfirm(String url, String message) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return false; - } - JsResultReceiver result = new JsResultReceiver(); - Message confirm = obtainMessage(JS_DIALOG, result); - confirm.getData().putString("message", message); - confirm.getData().putString("url", url); - confirm.getData().putInt("type", JsDialogHelper.CONFIRM); - sendMessageToUiThreadSync(confirm); - return result.mJsResult.getResult(); - } - - public String onJsPrompt(String url, String message, String defaultValue) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return null; - } - JsResultReceiver result = new JsResultReceiver(); - Message prompt = obtainMessage(JS_DIALOG, result); - prompt.getData().putString("message", message); - prompt.getData().putString("default", defaultValue); - prompt.getData().putString("url", url); - prompt.getData().putInt("type", JsDialogHelper.PROMPT); - sendMessageToUiThreadSync(prompt); - return result.mJsResult.getStringResult(); - } - - public boolean onJsBeforeUnload(String url, String message) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebChromeClient == null) { - return true; - } - JsResultReceiver result = new JsResultReceiver(); - Message unload = obtainMessage(JS_DIALOG, result); - unload.getData().putString("message", message); - unload.getData().putString("url", url); - unload.getData().putInt("type", JsDialogHelper.UNLOAD); - sendMessageToUiThreadSync(unload); - return result.mJsResult.getResult(); - } - - /** - * Called by WebViewCore to inform the Java side that the current origin - * has overflowed it's database quota. Called in the WebCore thread so - * posts a message to the UI thread that will prompt the WebChromeClient - * for what to do. On return back to C++ side, the WebCore thread will - * sleep pending a new quota value. - * @param url The URL that caused the quota overflow. - * @param databaseIdentifier The identifier of the database that the - * transaction that caused the overflow was running on. - * @param quota The current quota the origin is allowed. - * @param estimatedDatabaseSize The estimated size of the database. - * @param totalQuota is the sum of all origins' quota. - * @param quotaUpdater An instance of a class encapsulating a callback - * to WebViewCore to run when the decision to allow or deny more - * quota has been made. - */ - public void onExceededDatabaseQuota( - String url, String databaseIdentifier, long quota, - long estimatedDatabaseSize, long totalQuota, - WebStorage.QuotaUpdater quotaUpdater) { - if (mWebChromeClient == null) { - // Native-side logic prevents the quota being updated to a smaller - // value. - quotaUpdater.updateQuota(quota); - return; - } - - Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA); - HashMap<String, Object> map = new HashMap(); - map.put("databaseIdentifier", databaseIdentifier); - map.put("url", url); - map.put("quota", quota); - map.put("estimatedDatabaseSize", estimatedDatabaseSize); - map.put("totalQuota", totalQuota); - map.put("quotaUpdater", quotaUpdater); - exceededQuota.obj = map; - sendMessage(exceededQuota); - } - - /** - * Called by WebViewCore to inform the Java side that the appcache has - * exceeded its max size. - * @param requiredStorage is the amount of storage, in bytes, that would be - * needed in order for the last appcache operation to succeed. - * @param quota is the current quota (for all origins). - * @param quotaUpdater An instance of a class encapsulating a callback - * to WebViewCore to run when the decision to allow or deny a bigger - * app cache size has been made. - */ - public void onReachedMaxAppCacheSize(long requiredStorage, - long quota, WebStorage.QuotaUpdater quotaUpdater) { - if (mWebChromeClient == null) { - // Native-side logic prevents the quota being updated to a smaller - // value. - quotaUpdater.updateQuota(quota); - return; - } - - Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE); - HashMap<String, Object> map = new HashMap(); - map.put("requiredStorage", requiredStorage); - map.put("quota", quota); - map.put("quotaUpdater", quotaUpdater); - msg.obj = map; - sendMessage(msg); - } - - /** - * Called by WebViewCore to instruct the browser to display a prompt to ask - * the user to set the Geolocation permission state for the given origin. - * @param origin The origin requesting Geolocation permsissions. - * @param callback The callback to call once a permission state has been - * obtained. - */ - public void onGeolocationPermissionsShowPrompt(String origin, - GeolocationPermissions.Callback callback) { - if (mWebChromeClient == null) { - return; - } - - Message showMessage = - obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT); - HashMap<String, Object> map = new HashMap(); - map.put("origin", origin); - map.put("callback", callback); - showMessage.obj = map; - sendMessage(showMessage); - } - - /** - * Called by WebViewCore to instruct the browser to hide the Geolocation - * permissions prompt. - */ - public void onGeolocationPermissionsHidePrompt() { - if (mWebChromeClient == null) { - return; - } - - Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT); - sendMessage(hideMessage); - } - - /** - * Called by WebViewCore when we have a message to be added to the JavaScript - * error console. Sends a message to the Java side with the details. - * @param message The message to add to the console. - * @param lineNumber The lineNumber of the source file on which the error - * occurred. - * @param sourceID The filename of the source file in which the error - * occurred. - * @param msgLevel The message level, corresponding to the MessageLevel enum in - * WebCore/page/Console.h - */ - public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) { - if (mWebChromeClient == null) { - return; - } - - Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE); - msg.getData().putString("message", message); - msg.getData().putString("sourceID", sourceID); - msg.getData().putInt("lineNumber", lineNumber); - msg.getData().putInt("msgLevel", msgLevel); - sendMessage(msg); - } - - public boolean onJsTimeout() { - //always interrupt timedout JS by default - if (mWebChromeClient == null) { - return true; - } - JsResultReceiver result = new JsResultReceiver(); - Message timeout = obtainMessage(JS_TIMEOUT, result); - sendMessageToUiThreadSync(timeout); - return result.mJsResult.getResult(); - } - - public void getVisitedHistory(ValueCallback<String[]> callback) { - if (mWebChromeClient == null) { - return; - } - Message msg = obtainMessage(GET_VISITED_HISTORY); - msg.obj = callback; - sendMessage(msg); - } - - private static class UploadFileMessageData { - private UploadFile mCallback; - private String mAcceptType; - private String mCapture; - - public UploadFileMessageData(UploadFile uploadFile, String acceptType, String capture) { - mCallback = uploadFile; - mAcceptType = acceptType; - mCapture = capture; - } - - public UploadFile getUploadFile() { - return mCallback; - } - - public String getAcceptType() { - return mAcceptType; - } - - public String getCapture() { - return mCapture; - } - } - - private class UploadFile implements ValueCallback<Uri> { - private Uri mValue; - public void onReceiveValue(Uri value) { - mValue = value; - synchronized (CallbackProxy.this) { - CallbackProxy.this.notify(); - } - } - public Uri getResult() { - return mValue; - } - } - - /** - * Called by WebViewCore to open a file chooser. - */ - /* package */ Uri openFileChooser(String acceptType, String capture) { - if (mWebChromeClient == null) { - return null; - } - Message myMessage = obtainMessage(OPEN_FILE_CHOOSER); - UploadFile uploadFile = new UploadFile(); - UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType, capture); - myMessage.obj = data; - sendMessageToUiThreadSync(myMessage); - return uploadFile.getResult(); - } - - void onNewHistoryItem(WebHistoryItem item) { - if (mWebBackForwardListClient == null) { - return; - } - Message msg = obtainMessage(ADD_HISTORY_ITEM, item); - sendMessage(msg); - } - - void onIndexChanged(WebHistoryItem item, int index) { - if (mWebBackForwardListClient == null) { - return; - } - Message msg = obtainMessage(HISTORY_INDEX_CHANGED, index, 0, item); - sendMessage(msg); - } - - private synchronized void sendMessageToUiThreadSync(Message msg) { - sendMessage(msg); - WebCoreThreadWatchdog.pause(); - try { - wait(); - } catch (InterruptedException e) { - Log.e(LOGTAG, "Caught exception waiting for synchronous UI message to be processed"); - Log.e(LOGTAG, Log.getStackTraceString(e)); - } - WebCoreThreadWatchdog.resume(); - } -} diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java deleted file mode 100644 index e4d09a9..0000000 --- a/core/java/android/webkit/CertTool.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 android.webkit; - -import com.android.org.bouncycastle.asn1.ASN1Encoding; -import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest; -import com.android.org.bouncycastle.util.encoders.Base64; - -import android.content.Context; -import android.security.Credentials; -import android.security.KeyChain; -import android.util.Log; - -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.util.HashMap; - -final class CertTool { - private static final String LOGTAG = "CertTool"; - - private static final AlgorithmIdentifier MD5_WITH_RSA = - new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption); - - private static HashMap<String, String> sCertificateTypeMap; - static { - sCertificateTypeMap = new HashMap<String, String>(); - sCertificateTypeMap.put("application/x-x509-ca-cert", KeyChain.EXTRA_CERTIFICATE); - sCertificateTypeMap.put("application/x-x509-user-cert", KeyChain.EXTRA_CERTIFICATE); - sCertificateTypeMap.put("application/x-pkcs12", KeyChain.EXTRA_PKCS12); - } - - static String[] getKeyStrengthList() { - return new String[] {"High Grade", "Medium Grade"}; - } - - static String getSignedPublicKey(Context context, int index, String challenge) { - try { - KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); - generator.initialize((index == 0) ? 2048 : 1024); - KeyPair pair = generator.genKeyPair(); - - NetscapeCertRequest request = new NetscapeCertRequest(challenge, - MD5_WITH_RSA, pair.getPublic()); - request.sign(pair.getPrivate()); - byte[] signed = request.toASN1Primitive().getEncoded(ASN1Encoding.DER); - - Credentials.getInstance().install(context, pair); - return new String(Base64.encode(signed)); - } catch (Exception e) { - Log.w(LOGTAG, e); - } - return null; - } - - static void addCertificate(Context context, String type, byte[] value) { - Credentials.getInstance().install(context, type, value); - } - - static String getCertType(String mimeType) { - return sCertificateTypeMap.get(mimeType); - } - - private CertTool() {} -} diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java deleted file mode 100644 index d7a6806..0000000 --- a/core/java/android/webkit/ClientCertRequestHandler.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2011 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 android.webkit; - -import android.os.Handler; -import java.security.PrivateKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import com.android.org.conscrypt.NativeCrypto; -import com.android.org.conscrypt.OpenSSLKey; -import com.android.org.conscrypt.OpenSSLKeyHolder; - -/** - * ClientCertRequestHandler: class responsible for handling client - * certificate requests. This class is passed as a parameter to - * BrowserCallback.displayClientCertRequestDialog and is meant to - * receive the user's response. - * - * @hide - */ -public final class ClientCertRequestHandler extends Handler { - - private final BrowserFrame mBrowserFrame; - private final int mHandle; - private final String mHostAndPort; - private final SslClientCertLookupTable mTable; - ClientCertRequestHandler(BrowserFrame browserFrame, - int handle, - String host_and_port, - SslClientCertLookupTable table) { - mBrowserFrame = browserFrame; - mHandle = handle; - mHostAndPort = host_and_port; - mTable = table; - } - - /** - * Proceed with the specified private key and client certificate chain. - */ - public void proceed(PrivateKey privateKey, X509Certificate[] chain) { - try { - byte[][] chainBytes = NativeCrypto.encodeCertificates(chain); - mTable.Allow(mHostAndPort, privateKey, chainBytes); - - if (privateKey instanceof OpenSSLKeyHolder) { - OpenSSLKey pkey = ((OpenSSLKeyHolder) privateKey).getOpenSSLKey(); - setSslClientCertFromCtx(pkey.getPkeyContext(), chainBytes); - } else { - setSslClientCertFromPKCS8(privateKey.getEncoded(), chainBytes); - } - } catch (CertificateEncodingException e) { - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, 0, null); - return; - } - }); - } - } - - /** - * Proceed with the specified private key bytes and client certificate chain. - */ - private void setSslClientCertFromCtx(final long ctx, final byte[][] chainBytes) { - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, ctx, chainBytes); - } - }); - } - - /** - * Proceed with the specified private key context and client certificate chain. - */ - private void setSslClientCertFromPKCS8(final byte[] key, final byte[][] chainBytes) { - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, key, chainBytes); - } - }); - } - - /** - * Igore the request for now, the user may be prompted again. - */ - public void ignore() { - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, 0, null); - } - }); - } - - /** - * Cancel this request, remember the users negative choice. - */ - public void cancel() { - mTable.Deny(mHostAndPort); - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, 0, null); - } - }); - } -} diff --git a/core/java/android/webkit/CookieManagerClassic.java b/core/java/android/webkit/CookieManagerClassic.java deleted file mode 100644 index 36159e1..0000000 --- a/core/java/android/webkit/CookieManagerClassic.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.net.ParseException; -import android.net.WebAddress; -import android.os.AsyncTask; -import android.util.Log; - -class CookieManagerClassic extends CookieManager { - - private static CookieManagerClassic sRef; - - private static final String LOGTAG = "webkit"; - - private int mPendingCookieOperations = 0; - - private CookieManagerClassic() { - } - - public static synchronized CookieManagerClassic getInstance() { - if (sRef == null) { - sRef = new CookieManagerClassic(); - } - return sRef; - } - - @Override - public synchronized void setAcceptCookie(boolean accept) { - nativeSetAcceptCookie(accept); - } - - @Override - public synchronized boolean acceptCookie() { - return nativeAcceptCookie(); - } - - @Override - public void setCookie(String url, String value) { - setCookie(url, value, false); - } - - /** - * See {@link #setCookie(String, String)} - * @param url The URL for which the cookie is set - * @param value The value of the cookie, as a string, using the format of - * the 'Set-Cookie' HTTP response header - * @param privateBrowsing Whether to use the private browsing cookie jar - */ - void setCookie(String url, String value, boolean privateBrowsing) { - WebAddress uri; - try { - uri = new WebAddress(url); - } catch (ParseException ex) { - Log.e(LOGTAG, "Bad address: " + url); - return; - } - - nativeSetCookie(uri.toString(), value, privateBrowsing); - } - - @Override - public String getCookie(String url) { - return getCookie(url, false); - } - - @Override - public String getCookie(String url, boolean privateBrowsing) { - WebAddress uri; - try { - uri = new WebAddress(url); - } catch (ParseException ex) { - Log.e(LOGTAG, "Bad address: " + url); - return null; - } - - return nativeGetCookie(uri.toString(), privateBrowsing); - } - - @Override - public synchronized String getCookie(WebAddress uri) { - return nativeGetCookie(uri.toString(), false); - } - - /** - * Waits for pending operations to completed. - */ - void waitForCookieOperationsToComplete() { - // Note that this function is applicable for both the java - // and native http stacks, and works correctly with either. - synchronized (this) { - while (mPendingCookieOperations > 0) { - try { - wait(); - } catch (InterruptedException e) { } - } - } - } - - private synchronized void signalCookieOperationsComplete() { - mPendingCookieOperations--; - assert mPendingCookieOperations > -1; - notify(); - } - - private synchronized void signalCookieOperationsStart() { - mPendingCookieOperations++; - } - - @Override - public void removeSessionCookie() { - signalCookieOperationsStart(); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... none) { - nativeRemoveSessionCookie(); - signalCookieOperationsComplete(); - return null; - } - }.execute(); - } - - @Override - public void removeAllCookie() { - nativeRemoveAllCookie(); - } - - @Override - public synchronized boolean hasCookies() { - return hasCookies(false); - } - - @Override - public synchronized boolean hasCookies(boolean privateBrowsing) { - return nativeHasCookies(privateBrowsing); - } - - @Override - public void removeExpiredCookie() { - nativeRemoveExpiredCookie(); - } - - @Override - protected void flushCookieStore() { - nativeFlushCookieStore(); - } - - @Override - protected boolean allowFileSchemeCookiesImpl() { - return nativeAcceptFileSchemeCookies(); - } - - @Override - protected void setAcceptFileSchemeCookiesImpl(boolean accept) { - nativeSetAcceptFileSchemeCookies(accept); - } - - // Native functions - private static native boolean nativeAcceptCookie(); - private static native String nativeGetCookie(String url, boolean privateBrowsing); - private static native boolean nativeHasCookies(boolean privateBrowsing); - private static native void nativeRemoveAllCookie(); - private static native void nativeRemoveExpiredCookie(); - private static native void nativeRemoveSessionCookie(); - private static native void nativeSetAcceptCookie(boolean accept); - private static native void nativeSetCookie(String url, String value, boolean privateBrowsing); - private static native void nativeFlushCookieStore(); - private static native boolean nativeAcceptFileSchemeCookies(); - private static native void nativeSetAcceptFileSchemeCookies(boolean accept); -} diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java index 154a290..13aa43f 100644 --- a/core/java/android/webkit/CookieSyncManager.java +++ b/core/java/android/webkit/CookieSyncManager.java @@ -89,10 +89,6 @@ public final class CookieSyncManager extends WebSyncManager { if (context == null) { throw new IllegalArgumentException("Invalid context argument"); } - // TODO: Remove this workaround after webview classic is no longer supported. - if (WebViewFactory.getProvider().getClass().getName().contains("WebViewClassic")) { - WebViewDatabase.getInstance(context); - } setGetInstanceIsAllowed(); return getInstance(); diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java index 524f610..b5ca8c1 100644 --- a/core/java/android/webkit/DebugFlags.java +++ b/core/java/android/webkit/DebugFlags.java @@ -35,22 +35,4 @@ public class DebugFlags { public static final boolean URL_UTIL = false; public static final boolean WEB_SYNC_MANAGER = false; - // TODO: Delete these when WebViewClassic is moved - public static final boolean BROWSER_FRAME = false; - public static final boolean CACHE_MANAGER = false; - public static final boolean CALLBACK_PROXY = false; - public static final boolean COOKIE_MANAGER = false; - public static final boolean FRAME_LOADER = false; - public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE - public static final boolean LOAD_LISTENER = false; - public static final boolean MEASURE_PAGE_SWAP_FPS = false; - public static final boolean NETWORK = false; - public static final boolean SSL_ERROR_HANDLER = false; - public static final boolean STREAM_LOADER = false; - public static final boolean WEB_BACK_FORWARD_LIST = false; - public static final boolean WEB_SETTINGS = false; - public static final boolean WEB_VIEW = false; - public static final boolean WEB_VIEW_CORE = false; - - } diff --git a/core/java/android/webkit/DeviceMotionAndOrientationManager.java b/core/java/android/webkit/DeviceMotionAndOrientationManager.java deleted file mode 100644 index ea1e9ff..0000000 --- a/core/java/android/webkit/DeviceMotionAndOrientationManager.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -/** - * This class is simply a container for the methods used to implement DeviceMotion and - * DeviceOrientation, including the mock DeviceOrientationClient for use in LayoutTests. - * - * This could be part of WebViewCore, but have moved it to its own class to - * avoid bloat there. - */ -final class DeviceMotionAndOrientationManager { - private WebViewCore mWebViewCore; - - public DeviceMotionAndOrientationManager(WebViewCore webViewCore) { - mWebViewCore = webViewCore; - } - - /** - * Sets that the Page for this WebViewCore should use a mock DeviceOrientation - * client. - */ - public void setUseMock() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - nativeSetUseMock(mWebViewCore); - } - - /** - * Set the position for the mock DeviceOrientation service for this WebViewCore. - */ - public void setMockOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta, - double beta, boolean canProvideGamma, double gamma) { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - nativeSetMockOrientation(mWebViewCore, canProvideAlpha, alpha, canProvideBeta, beta, - canProvideGamma, gamma); - } - - // We only provide accelerationIncludingGravity. - public void onMotionChange(Double x, Double y, Double z, double interval) { - nativeOnMotionChange(mWebViewCore, - x != null, x != null ? x.doubleValue() : 0.0, - y != null, y != null ? y.doubleValue() : 0.0, - z != null, z != null ? z.doubleValue() : 0.0, - interval); - } - public void onOrientationChange(Double alpha, Double beta, Double gamma) { - nativeOnOrientationChange(mWebViewCore, - alpha != null, alpha != null ? alpha.doubleValue() : 0.0, - beta != null, beta != null ? beta.doubleValue() : 0.0, - gamma != null, gamma != null ? gamma.doubleValue() : 0.0); - } - - // Native functions - private static native void nativeSetUseMock(WebViewCore webViewCore); - private static native void nativeSetMockOrientation(WebViewCore webViewCore, - boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, - boolean canProvideGamma, double gamma); - private static native void nativeOnMotionChange(WebViewCore webViewCore, - boolean canProvideX, double x, boolean canProvideY, double y, - boolean canProvideZ, double z, double interval); - private static native void nativeOnOrientationChange(WebViewCore webViewCore, - boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, - boolean canProvideGamma, double gamma); -} diff --git a/core/java/android/webkit/DeviceMotionService.java b/core/java/android/webkit/DeviceMotionService.java deleted file mode 100644 index 9121429..0000000 --- a/core/java/android/webkit/DeviceMotionService.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Handler; -import android.os.Message; -import android.webkit.DeviceMotionAndOrientationManager; -import java.lang.Runnable; -import java.util.List; - - -final class DeviceMotionService implements SensorEventListener { - private DeviceMotionAndOrientationManager mManager; - private boolean mIsRunning; - private Handler mHandler; - private SensorManager mSensorManager; - private Context mContext; - private boolean mHaveSentErrorEvent; - private Runnable mUpdateRunnable; - private float mLastAcceleration[]; - - private static final int INTERVAL_MILLIS = 100; - - public DeviceMotionService(DeviceMotionAndOrientationManager manager, Context context) { - mManager = manager; - assert(mManager != null); - mContext = context; - assert(mContext != null); - } - - public void start() { - mIsRunning = true; - registerForSensor(); - } - - public void stop() { - mIsRunning = false; - stopSendingUpdates(); - unregisterFromSensor(); - } - - public void suspend() { - if (mIsRunning) { - stopSendingUpdates(); - unregisterFromSensor(); - } - } - - public void resume() { - if (mIsRunning) { - registerForSensor(); - } - } - - private void sendErrorEvent() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - // The spec requires that each listener receives the error event only once. - if (mHaveSentErrorEvent) - return; - mHaveSentErrorEvent = true; - createHandler(); - mHandler.post(new Runnable() { - @Override - public void run() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - if (mIsRunning) { - // The special case of all nulls is used to signify a failure to get data. - mManager.onMotionChange(null, null, null, 0.0); - } - } - }); - } - - private void createHandler() { - if (mHandler != null) { - return; - } - - mHandler = new Handler(); - mUpdateRunnable = new Runnable() { - @Override - public void run() { - assert mIsRunning; - mManager.onMotionChange(new Double(mLastAcceleration[0]), - new Double(mLastAcceleration[1]), new Double(mLastAcceleration[2]), - INTERVAL_MILLIS); - mHandler.postDelayed(mUpdateRunnable, INTERVAL_MILLIS); - // Now that we have successfully sent some data, reset whether we've sent an error. - mHaveSentErrorEvent = false; - } - }; - } - - private void startSendingUpdates() { - createHandler(); - mUpdateRunnable.run(); - } - - private void stopSendingUpdates() { - mHandler.removeCallbacks(mUpdateRunnable); - mLastAcceleration = null; - } - - private void registerForSensor() { - if (!registerForAccelerometerSensor()) { - sendErrorEvent(); - } - } - - private SensorManager getSensorManager() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - if (mSensorManager == null) { - mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); - } - return mSensorManager; - } - - private boolean registerForAccelerometerSensor() { - List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER); - if (sensors.isEmpty()) { - return false; - } - createHandler(); - // TODO: Consider handling multiple sensors. - return getSensorManager().registerListener( - this, sensors.get(0), SensorManager.SENSOR_DELAY_UI, mHandler); - } - - private void unregisterFromSensor() { - getSensorManager().unregisterListener(this); - } - - /** - * SensorEventListener implementation. - * Callbacks happen on the thread on which we registered - the WebCore thread. - */ - @Override - public void onSensorChanged(SensorEvent event) { - assert(event.values.length == 3); - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - assert(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER); - - // We may get callbacks after the call to getSensorManager().unregisterListener() returns. - if (!mIsRunning) { - return; - } - - boolean firstData = mLastAcceleration == null; - mLastAcceleration = event.values; - if (firstData) { - startSendingUpdates(); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - } -} diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java deleted file mode 100644 index a4d240d..0000000 --- a/core/java/android/webkit/DeviceOrientationService.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Handler; -import android.webkit.DeviceMotionAndOrientationManager; -import java.lang.Runnable; -import java.util.List; - - -final class DeviceOrientationService implements SensorEventListener { - // The gravity vector expressed in the body frame. - private float[] mGravityVector; - // The geomagnetic vector expressed in the body frame. - private float[] mMagneticFieldVector; - - private DeviceMotionAndOrientationManager mManager; - private boolean mIsRunning; - private Handler mHandler; - private SensorManager mSensorManager; - private Context mContext; - private Double mAlpha; - private Double mBeta; - private Double mGamma; - private boolean mHaveSentErrorEvent; - - private static final double DELTA_DEGRESS = 1.0; - - public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) { - mManager = manager; - assert(mManager != null); - mContext = context; - assert(mContext != null); - } - - public void start() { - mIsRunning = true; - registerForSensors(); - } - - public void stop() { - mIsRunning = false; - unregisterFromSensors(); - } - - public void suspend() { - if (mIsRunning) { - unregisterFromSensors(); - } - } - - public void resume() { - if (mIsRunning) { - registerForSensors(); - } - } - - private void sendErrorEvent() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - // The spec requires that each listener receives the error event only once. - if (mHaveSentErrorEvent) - return; - mHaveSentErrorEvent = true; - mHandler.post(new Runnable() { - @Override - public void run() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - if (mIsRunning) { - // The special case of all nulls is used to signify a failure to get data. - mManager.onOrientationChange(null, null, null); - } - } - }); - } - - private void registerForSensors() { - if (mHandler == null) { - mHandler = new Handler(); - } - if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) { - unregisterFromSensors(); - sendErrorEvent(); - } - } - - private void getOrientationUsingGetRotationMatrix() { - if (mGravityVector == null || mMagneticFieldVector == null) { - return; - } - - // Get the rotation matrix. - // The rotation matrix that transforms from the body frame to the earth frame. - float[] deviceRotationMatrix = new float[9]; - if (!SensorManager.getRotationMatrix( - deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) { - return; - } - - // Convert rotation matrix to rotation angles. - // Assuming that the rotations are appied in the order listed at - // http://developer.android.com/reference/android/hardware/SensorEvent.html#values - // the rotations are applied about the same axes and in the same order as required by the - // API. The only conversions are sign changes as follows. - // The angles are in radians - float[] rotationAngles = new float[3]; - SensorManager.getOrientation(deviceRotationMatrix, rotationAngles); - double alpha = Math.toDegrees(-rotationAngles[0]); - while (alpha < 0.0) { alpha += 360.0; } // [0, 360) - double beta = Math.toDegrees(-rotationAngles[1]); - while (beta < -180.0) { beta += 360.0; } // [-180, 180) - double gamma = Math.toDegrees(rotationAngles[2]); - while (gamma < -90.0) { gamma += 360.0; } // [-90, 90) - - maybeSendChange(alpha, beta, gamma); - } - - private SensorManager getSensorManager() { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - if (mSensorManager == null) { - mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); - } - return mSensorManager; - } - - private boolean registerForAccelerometerSensor() { - List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER); - if (sensors.isEmpty()) { - return false; - } - // TODO: Consider handling multiple sensors. - return getSensorManager().registerListener( - this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler); - } - - private boolean registerForMagneticFieldSensor() { - List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD); - if (sensors.isEmpty()) { - return false; - } - // TODO: Consider handling multiple sensors. - return getSensorManager().registerListener( - this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler); - } - - private void unregisterFromSensors() { - getSensorManager().unregisterListener(this); - } - - private void maybeSendChange(double alpha, double beta, double gamma) { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - if (mAlpha == null || mBeta == null || mGamma == null - || Math.abs(alpha - mAlpha) > DELTA_DEGRESS - || Math.abs(beta - mBeta) > DELTA_DEGRESS - || Math.abs(gamma - mGamma) > DELTA_DEGRESS) { - mAlpha = alpha; - mBeta = beta; - mGamma = gamma; - mManager.onOrientationChange(mAlpha, mBeta, mGamma); - // Now that we have successfully sent some data, reset whether we've sent an error. - mHaveSentErrorEvent = false; - } - } - - /** - * SensorEventListener implementation. - * Callbacks happen on the thread on which we registered - the WebCore thread. - */ - @Override - public void onSensorChanged(SensorEvent event) { - assert(event.values.length == 3); - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - - // We may get callbacks after the call to getSensorManager().unregisterListener() returns. - if (!mIsRunning) { - return; - } - - switch (event.sensor.getType()) { - case Sensor.TYPE_ACCELEROMETER: - if (mGravityVector == null) { - mGravityVector = new float[3]; - } - mGravityVector[0] = event.values[0]; - mGravityVector[1] = event.values[1]; - mGravityVector[2] = event.values[2]; - getOrientationUsingGetRotationMatrix(); - break; - case Sensor.TYPE_MAGNETIC_FIELD: - if (mMagneticFieldVector == null) { - mMagneticFieldVector = new float[3]; - } - mMagneticFieldVector[0] = event.values[0]; - mMagneticFieldVector[1] = event.values[1]; - mMagneticFieldVector[2] = event.values[2]; - getOrientationUsingGetRotationMatrix(); - break; - default: - assert(false); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - } -} diff --git a/core/java/android/webkit/GeolocationPermissionsClassic.java b/core/java/android/webkit/GeolocationPermissionsClassic.java deleted file mode 100644 index 8a9df39..0000000 --- a/core/java/android/webkit/GeolocationPermissionsClassic.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.os.Handler; -import android.os.Message; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.Vector; - -// This class is the Java counterpart of the WebKit C++ GeolocationPermissions -// class. It simply marshals calls from the UI thread to the WebKit thread. -final class GeolocationPermissionsClassic extends GeolocationPermissions { - private Handler mHandler; - private Handler mUIHandler; - - // A queue to store messages until the handler is ready. - private Vector<Message> mQueuedMessages; - - // Message ids - static final int GET_ORIGINS = 0; - static final int GET_ALLOWED = 1; - static final int CLEAR = 2; - static final int ALLOW = 3; - static final int CLEAR_ALL = 4; - - // Message ids on the UI thread - static final int RETURN_ORIGINS = 0; - static final int RETURN_ALLOWED = 1; - - private static final String ORIGINS = "origins"; - private static final String ORIGIN = "origin"; - private static final String CALLBACK = "callback"; - private static final String ALLOWED = "allowed"; - - // Global instance - private static GeolocationPermissionsClassic sInstance; - - public static GeolocationPermissionsClassic getInstance() { - if (sInstance == null) { - sInstance = new GeolocationPermissionsClassic(); - } - return sInstance; - } - - /** - * Creates the UI message handler. Must be called on the UI thread. - * @hide - */ - public void createUIHandler() { - if (mUIHandler == null) { - mUIHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - // Runs on the UI thread. - switch (msg.what) { - case RETURN_ORIGINS: { - Map values = (Map) msg.obj; - Set<String> origins = (Set<String>) values.get(ORIGINS); - ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK); - callback.onReceiveValue(origins); - } break; - case RETURN_ALLOWED: { - Map values = (Map) msg.obj; - Boolean allowed = (Boolean) values.get(ALLOWED); - ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK); - callback.onReceiveValue(allowed); - } break; - } - } - }; - } - } - - /** - * Creates the message handler. Must be called on the WebKit thread. - * @hide - */ - public synchronized void createHandler() { - if (mHandler == null) { - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - // Runs on the WebKit thread. - switch (msg.what) { - case GET_ORIGINS: { - Set origins = nativeGetOrigins(); - ValueCallback callback = (ValueCallback) msg.obj; - Map values = new HashMap<String, Object>(); - values.put(CALLBACK, callback); - values.put(ORIGINS, origins); - postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); - } break; - case GET_ALLOWED: { - Map values = (Map) msg.obj; - String origin = (String) values.get(ORIGIN); - ValueCallback callback = (ValueCallback) values.get(CALLBACK); - boolean allowed = nativeGetAllowed(origin); - Map retValues = new HashMap<String, Object>(); - retValues.put(CALLBACK, callback); - retValues.put(ALLOWED, Boolean.valueOf(allowed)); - postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues)); - } break; - case CLEAR: - nativeClear((String) msg.obj); - break; - case ALLOW: - nativeAllow((String) msg.obj); - break; - case CLEAR_ALL: - nativeClearAll(); - break; - } - } - }; - - // Handle the queued messages - if (mQueuedMessages != null) { - while (!mQueuedMessages.isEmpty()) { - mHandler.sendMessage(mQueuedMessages.remove(0)); - } - mQueuedMessages = null; - } - } - } - - /** - * Utility function to send a message to our handler. - */ - private synchronized void postMessage(Message msg) { - if (mHandler == null) { - if (mQueuedMessages == null) { - mQueuedMessages = new Vector<Message>(); - } - mQueuedMessages.add(msg); - } else { - mHandler.sendMessage(msg); - } - } - - /** - * Utility function to send a message to the handler on the UI thread - */ - private void postUIMessage(Message msg) { - if (mUIHandler != null) { - mUIHandler.sendMessage(msg); - } - } - - // Note that we represent the origins as strings. These are created using - // WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules' - // (Database, Geolocation etc) do so, it's safe to match up origins based - // on this string. - @Override - public void getOrigins(ValueCallback<Set<String> > callback) { - if (callback != null) { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - Set origins = nativeGetOrigins(); - callback.onReceiveValue(origins); - } else { - postMessage(Message.obtain(null, GET_ORIGINS, callback)); - } - } - } - - @Override - public void getAllowed(String origin, ValueCallback<Boolean> callback) { - if (callback == null) { - return; - } - if (origin == null) { - callback.onReceiveValue(null); - return; - } - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - boolean allowed = nativeGetAllowed(origin); - callback.onReceiveValue(Boolean.valueOf(allowed)); - } else { - Map values = new HashMap<String, Object>(); - values.put(ORIGIN, origin); - values.put(CALLBACK, callback); - postMessage(Message.obtain(null, GET_ALLOWED, values)); - } - } - - // This method may be called before the WebKit - // thread has intialized the message handler. Messages will be queued until - // this time. - @Override - public void clear(String origin) { - // Called on the UI thread. - postMessage(Message.obtain(null, CLEAR, origin)); - } - - // This method may be called before the WebKit - // thread has intialized the message handler. Messages will be queued until - // this time. - @Override - public void allow(String origin) { - // Called on the UI thread. - postMessage(Message.obtain(null, ALLOW, origin)); - } - - @Override - public void clearAll() { - // Called on the UI thread. - postMessage(Message.obtain(null, CLEAR_ALL)); - } - - GeolocationPermissionsClassic() {} - - // Native functions, run on the WebKit thread. - private static native Set nativeGetOrigins(); - private static native boolean nativeGetAllowed(String origin); - private static native void nativeClear(String origin); - private static native void nativeAllow(String origin); - private static native void nativeClearAll(); -} diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java deleted file mode 100644 index 225053b..0000000 --- a/core/java/android/webkit/GeolocationService.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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 android.webkit; - -import android.app.ActivityThread; -import android.content.Context; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; -import android.os.Bundle; -import android.util.Log; -import android.webkit.WebViewCore; - - -/** - * Implements the Java side of GeolocationServiceAndroid. - */ -final class GeolocationService implements LocationListener { - - // Log tag - private static final String TAG = "geolocationService"; - - private long mNativeObject; - private LocationManager mLocationManager; - private boolean mIsGpsEnabled; - private boolean mIsRunning; - private boolean mIsNetworkProviderAvailable; - private boolean mIsGpsProviderAvailable; - - /** - * Constructor - * @param context The context from which we obtain the system service. - * @param nativeObject The native object to which this object will report position updates and - * errors. - */ - public GeolocationService(Context context, long nativeObject) { - mNativeObject = nativeObject; - // Register newLocationAvailable with platform service. - mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - if (mLocationManager == null) { - Log.e(TAG, "Could not get location manager."); - } - } - - /** - * Start listening for location updates. - */ - public boolean start() { - registerForLocationUpdates(); - mIsRunning = true; - return mIsNetworkProviderAvailable || mIsGpsProviderAvailable; - } - - /** - * Stop listening for location updates. - */ - public void stop() { - unregisterFromLocationUpdates(); - mIsRunning = false; - } - - /** - * Sets whether to use the GPS. - * @param enable Whether to use the GPS. - */ - public void setEnableGps(boolean enable) { - if (mIsGpsEnabled != enable) { - mIsGpsEnabled = enable; - if (mIsRunning) { - // There's no way to unregister from a single provider, so we can - // only unregister from all, then reregister with all but the GPS. - unregisterFromLocationUpdates(); - registerForLocationUpdates(); - // Check that the providers are still available after we re-register. - maybeReportError("The last location provider is no longer available"); - } - } - } - - /** - * LocationListener implementation. - * Called when the location has changed. - * @param location The new location, as a Location object. - */ - public void onLocationChanged(Location location) { - // Callbacks from the system location sevice are queued to this thread, so it's possible - // that we receive callbacks after unregistering. At this point, the native object will no - // longer exist. - if (mIsRunning) { - nativeNewLocationAvailable(mNativeObject, location); - } - } - - /** - * LocationListener implementation. - * Called when the provider status changes. - * @param provider The name of the provider. - * @param status The new status of the provider. - * @param extras an optional Bundle with provider specific data. - */ - public void onStatusChanged(String providerName, int status, Bundle extras) { - boolean isAvailable = (status == LocationProvider.AVAILABLE); - if (LocationManager.NETWORK_PROVIDER.equals(providerName)) { - mIsNetworkProviderAvailable = isAvailable; - } else if (LocationManager.GPS_PROVIDER.equals(providerName)) { - mIsGpsProviderAvailable = isAvailable; - } - maybeReportError("The last location provider is no longer available"); - } - - /** - * LocationListener implementation. - * Called when the provider is enabled. - * @param provider The name of the location provider that is now enabled. - */ - public void onProviderEnabled(String providerName) { - // No need to notify the native side. It's enough to start sending - // valid position fixes again. - if (LocationManager.NETWORK_PROVIDER.equals(providerName)) { - mIsNetworkProviderAvailable = true; - } else if (LocationManager.GPS_PROVIDER.equals(providerName)) { - mIsGpsProviderAvailable = true; - } - } - - /** - * LocationListener implementation. - * Called when the provider is disabled. - * @param provider The name of the location provider that is now disabled. - */ - public void onProviderDisabled(String providerName) { - if (LocationManager.NETWORK_PROVIDER.equals(providerName)) { - mIsNetworkProviderAvailable = false; - } else if (LocationManager.GPS_PROVIDER.equals(providerName)) { - mIsGpsProviderAvailable = false; - } - maybeReportError("The last location provider was disabled"); - } - - /** - * Registers this object with the location service. - */ - private void registerForLocationUpdates() { - try { - // Registration may fail if providers are not present on the device. - try { - mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); - mIsNetworkProviderAvailable = true; - } catch(IllegalArgumentException e) { } - if (mIsGpsEnabled) { - try { - mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); - mIsGpsProviderAvailable = true; - } catch(IllegalArgumentException e) { } - } - } catch(SecurityException e) { - Log.e(TAG, "Caught security exception registering for location updates from system. " + - "This should only happen in DumpRenderTree."); - } - } - - /** - * Unregisters this object from the location service. - */ - private void unregisterFromLocationUpdates() { - mLocationManager.removeUpdates(this); - mIsNetworkProviderAvailable = false; - mIsGpsProviderAvailable = false; - } - - /** - * Reports an error if neither the network nor the GPS provider is available. - */ - private void maybeReportError(String message) { - // Callbacks from the system location sevice are queued to this thread, so it's possible - // that we receive callbacks after unregistering. At this point, the native object will no - // longer exist. - if (mIsRunning && !mIsNetworkProviderAvailable && !mIsGpsProviderAvailable) { - nativeNewErrorAvailable(mNativeObject, message); - } - } - - // Native functions - private static native void nativeNewLocationAvailable(long nativeObject, Location location); - private static native void nativeNewErrorAvailable(long nativeObject, String message); -} diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java deleted file mode 100644 index 17eb2df..0000000 --- a/core/java/android/webkit/HTML5Audio.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Log; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; - -/** - * HTML5 support class for Audio. - * - * This class runs almost entirely on the WebCore thread. The exception is when - * accessing the WebView object to determine whether private browsing is - * enabled. - */ -class HTML5Audio extends Handler - implements MediaPlayer.OnBufferingUpdateListener, - MediaPlayer.OnCompletionListener, - MediaPlayer.OnErrorListener, - MediaPlayer.OnPreparedListener, - MediaPlayer.OnSeekCompleteListener, - AudioManager.OnAudioFocusChangeListener { - // Logging tag. - private static final String LOGTAG = "HTML5Audio"; - - private MediaPlayer mMediaPlayer; - - // The C++ MediaPlayerPrivateAndroid object. - private int mNativePointer; - // The private status of the view that created this player - private IsPrivateBrowsingEnabledGetter mIsPrivateBrowsingEnabledGetter; - - private static int IDLE = 0; - private static int INITIALIZED = 1; - private static int PREPARED = 2; - private static int STARTED = 4; - private static int COMPLETE = 5; - private static int PAUSED = 6; - private static int PAUSED_TRANSITORILY = 7; - private static int STOPPED = -2; - private static int ERROR = -1; - - private int mState = IDLE; - - private String mUrl; - private boolean mAskToPlay = false; - private boolean mLoopEnabled = false; - private boolean mProcessingOnEnd = false; - private Context mContext; - - // Timer thread -> UI thread - private static final int TIMEUPDATE = 100; - - private static final String COOKIE = "Cookie"; - private static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; - - // The spec says the timer should fire every 250 ms or less. - private static final int TIMEUPDATE_PERIOD = 250; // ms - // The timer for timeupate events. - // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate - private Timer mTimer; - private final class TimeupdateTask extends TimerTask { - @Override - public void run() { - HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget(); - } - } - - // Helper class to determine whether private browsing is enabled in the - // given WebView. Queries the WebView on the UI thread. Calls to get() - // block until the data is available. - private class IsPrivateBrowsingEnabledGetter { - private boolean mIsReady; - private boolean mIsPrivateBrowsingEnabled; - IsPrivateBrowsingEnabledGetter(Looper uiThreadLooper, final WebViewClassic webView) { - new Handler(uiThreadLooper).post(new Runnable() { - @Override - public void run() { - synchronized(IsPrivateBrowsingEnabledGetter.this) { - mIsPrivateBrowsingEnabled = webView.isPrivateBrowsingEnabled(); - mIsReady = true; - IsPrivateBrowsingEnabledGetter.this.notify(); - } - } - }); - } - synchronized boolean get() { - while (!mIsReady) { - try { - wait(); - } catch (InterruptedException e) { - } - } - return mIsPrivateBrowsingEnabled; - } - }; - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case TIMEUPDATE: { - try { - if (mState != ERROR && mMediaPlayer.isPlaying()) { - int position = mMediaPlayer.getCurrentPosition(); - nativeOnTimeupdate(position, mNativePointer); - } - } catch (IllegalStateException e) { - mState = ERROR; - } - } - } - } - - // event listeners for MediaPlayer - // Those are called from the same thread we created the MediaPlayer - // (i.e. the webviewcore thread here) - - // MediaPlayer.OnBufferingUpdateListener - @Override - public void onBufferingUpdate(MediaPlayer mp, int percent) { - nativeOnBuffering(percent, mNativePointer); - } - - // MediaPlayer.OnCompletionListener; - @Override - public void onCompletion(MediaPlayer mp) { - mState = COMPLETE; - mProcessingOnEnd = true; - nativeOnEnded(mNativePointer); - mProcessingOnEnd = false; - if (mLoopEnabled == true) { - nativeOnRequestPlay(mNativePointer); - mLoopEnabled = false; - } - } - - // MediaPlayer.OnErrorListener - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - mState = ERROR; - resetMediaPlayer(); - mState = IDLE; - return false; - } - - // MediaPlayer.OnPreparedListener - @Override - public void onPrepared(MediaPlayer mp) { - mState = PREPARED; - if (mTimer != null) { - mTimer.schedule(new TimeupdateTask(), - TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); - } - nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer); - if (mAskToPlay) { - mAskToPlay = false; - play(); - } - } - - // MediaPlayer.OnSeekCompleteListener - @Override - public void onSeekComplete(MediaPlayer mp) { - nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer); - } - - - /** - * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. - */ - public HTML5Audio(WebViewCore webViewCore, int nativePtr) { - // Save the native ptr - mNativePointer = nativePtr; - resetMediaPlayer(); - mContext = webViewCore.getContext(); - mIsPrivateBrowsingEnabledGetter = new IsPrivateBrowsingEnabledGetter( - webViewCore.getContext().getMainLooper(), webViewCore.getWebViewClassic()); - } - - private void resetMediaPlayer() { - if (mMediaPlayer == null) { - mMediaPlayer = new MediaPlayer(); - } else { - mMediaPlayer.reset(); - } - mMediaPlayer.setOnBufferingUpdateListener(this); - mMediaPlayer.setOnCompletionListener(this); - mMediaPlayer.setOnErrorListener(this); - mMediaPlayer.setOnPreparedListener(this); - mMediaPlayer.setOnSeekCompleteListener(this); - - if (mTimer != null) { - mTimer.cancel(); - } - mTimer = new Timer(); - mState = IDLE; - } - - private void setDataSource(String url) { - mUrl = url; - try { - if (mState != IDLE) { - resetMediaPlayer(); - } - String cookieValue = CookieManager.getInstance().getCookie( - url, mIsPrivateBrowsingEnabledGetter.get()); - Map<String, String> headers = new HashMap<String, String>(); - - if (cookieValue != null) { - headers.put(COOKIE, cookieValue); - } - if (mIsPrivateBrowsingEnabledGetter.get()) { - headers.put(HIDE_URL_LOGS, "true"); - } - - mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers); - mState = INITIALIZED; - mMediaPlayer.prepareAsync(); - } catch (IOException e) { - String debugUrl = url.length() > 128 ? url.substring(0, 128) + "..." : url; - Log.e(LOGTAG, "couldn't load the resource: "+ debugUrl +" exc: " + e); - resetMediaPlayer(); - } - } - - @Override - public void onAudioFocusChange(int focusChange) { - switch (focusChange) { - case AudioManager.AUDIOFOCUS_GAIN: - // resume playback - if (mMediaPlayer == null) { - resetMediaPlayer(); - } else if (mState == PAUSED_TRANSITORILY && !mMediaPlayer.isPlaying()) { - mMediaPlayer.start(); - mState = STARTED; - } - break; - - case AudioManager.AUDIOFOCUS_LOSS: - // Lost focus for an unbounded amount of time: stop playback. - if (mState != ERROR && mMediaPlayer.isPlaying()) { - mMediaPlayer.stop(); - mState = STOPPED; - } - break; - - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - // Lost focus for a short time, but we have to stop - // playback. - if (mState != ERROR && mMediaPlayer.isPlaying()) { - pause(PAUSED_TRANSITORILY); - } - break; - } - } - - - private void play() { - if (mState == COMPLETE && mLoopEnabled == true) { - // Play it again, Sam - mMediaPlayer.start(); - mState = STARTED; - return; - } - - if (((mState >= ERROR && mState < PREPARED)) && mUrl != null) { - resetMediaPlayer(); - setDataSource(mUrl); - mAskToPlay = true; - } - - if (mState >= PREPARED) { - AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, - AudioManager.AUDIOFOCUS_GAIN); - - if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - mMediaPlayer.start(); - mState = STARTED; - } - } - } - - private void pause() { - pause(PAUSED); - } - - private void pause(int state) { - if (mState == STARTED) { - if (mTimer != null) { - mTimer.purge(); - } - mMediaPlayer.pause(); - mState = state; - } - } - - private void seek(int msec) { - if (mProcessingOnEnd == true && mState == COMPLETE && msec == 0) { - mLoopEnabled = true; - } - if (mState >= PREPARED) { - mMediaPlayer.seekTo(msec); - } - } - - /** - * Called only over JNI when WebKit is happy to - * destroy the media player. - */ - private void teardown() { - mMediaPlayer.release(); - mMediaPlayer = null; - mState = ERROR; - mNativePointer = 0; - } - - private float getMaxTimeSeekable() { - if (mState >= PREPARED) { - return mMediaPlayer.getDuration() / 1000.0f; - } else { - return 0; - } - } - - private native void nativeOnBuffering(int percent, int nativePointer); - private native void nativeOnEnded(int nativePointer); - private native void nativeOnRequestPlay(int nativePointer); - private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); - private native void nativeOnTimeupdate(int position, int nativePointer); - -} diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java deleted file mode 100644 index 6fb32c8..0000000 --- a/core/java/android/webkit/HTML5VideoFullScreen.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.Context; -import android.media.MediaPlayer; -import android.media.Metadata; -import android.util.Log; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.MediaController; -import android.widget.MediaController.MediaPlayerControl; - - -/** - * @hide This is only used by the browser - */ -public class HTML5VideoFullScreen extends HTML5VideoView - implements MediaPlayerControl, MediaPlayer.OnPreparedListener, - View.OnTouchListener { - - // Add this sub-class to handle the resizing when rotating screen. - private class VideoSurfaceView extends SurfaceView { - - public VideoSurfaceView(Context context) { - super(context); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getDefaultSize(mVideoWidth, widthMeasureSpec); - int height = getDefaultSize(mVideoHeight, heightMeasureSpec); - if (mVideoWidth > 0 && mVideoHeight > 0) { - if ( mVideoWidth * height > width * mVideoHeight ) { - height = width * mVideoHeight / mVideoWidth; - } else if ( mVideoWidth * height < width * mVideoHeight ) { - width = height * mVideoWidth / mVideoHeight; - } - } - setMeasuredDimension(width, height); - } - } - - // This view will contain the video. - private VideoSurfaceView mVideoSurfaceView; - - // We need the full screen state to decide which surface to render to and - // when to create the MediaPlayer accordingly. - static final int FULLSCREEN_OFF = 0; - static final int FULLSCREEN_SURFACECREATING = 1; - static final int FULLSCREEN_SURFACECREATED = 2; - - private int mFullScreenMode; - // The Media Controller only used for full screen mode - private MediaController mMediaController; - - // SurfaceHolder for full screen - private SurfaceHolder mSurfaceHolder = null; - - // Data only for MediaController - private boolean mCanSeekBack; - private boolean mCanSeekForward; - private boolean mCanPause; - private int mCurrentBufferPercentage; - - // The progress view. - private static View mProgressView; - // The container for the progress view and video view - private static FrameLayout mLayout; - - // The video size will be ready when prepared. Used to make sure the aspect - // ratio is correct. - private int mVideoWidth; - private int mVideoHeight; - private boolean mPlayingWhenDestroyed = false; - SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() - { - @Override - public void surfaceChanged(SurfaceHolder holder, int format, - int w, int h) - { - if (mPlayer != null && mMediaController != null - && mCurrentState == STATE_PREPARED) { - if (mMediaController.isShowing()) { - // ensure the controller will get repositioned later - mMediaController.hide(); - } - mMediaController.show(); - } - } - - @Override - public void surfaceCreated(SurfaceHolder holder) - { - mSurfaceHolder = holder; - mFullScreenMode = FULLSCREEN_SURFACECREATED; - - prepareForFullScreen(); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) - { - mPlayingWhenDestroyed = mPlayer.isPlaying(); - pauseAndDispatch(mProxy); - // We need to set the display to null before switching into inline - // mode to avoid error. - mPlayer.setDisplay(null); - mSurfaceHolder = null; - if (mMediaController != null) { - mMediaController.hide(); - } - } - }; - - MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = - new MediaPlayer.OnVideoSizeChangedListener() { - @Override - public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { - mVideoWidth = mp.getVideoWidth(); - mVideoHeight = mp.getVideoHeight(); - if (mVideoWidth != 0 && mVideoHeight != 0) { - mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight); - } - } - }; - - private SurfaceView getSurfaceView() { - return mVideoSurfaceView; - } - - HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare) { - mVideoSurfaceView = new VideoSurfaceView(context); - mFullScreenMode = FULLSCREEN_OFF; - mVideoWidth = 0; - mVideoHeight = 0; - init(videoLayerId, position, skipPrepare); - } - - private void setMediaController(MediaController m) { - mMediaController = m; - attachMediaController(); - } - - private void attachMediaController() { - if (mPlayer != null && mMediaController != null) { - mMediaController.setMediaPlayer(this); - mMediaController.setAnchorView(mVideoSurfaceView); - //Will be enabled when prepared - mMediaController.setEnabled(false); - } - } - - @Override - public void decideDisplayMode() { - mPlayer.setDisplay(mSurfaceHolder); - } - - private void prepareForFullScreen() { - MediaController mc = new FullScreenMediaController(mProxy.getContext(), mLayout); - mc.setSystemUiVisibility(mLayout.getSystemUiVisibility()); - setMediaController(mc); - mPlayer.setScreenOnWhilePlaying(true); - mPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); - prepareDataAndDisplayMode(mProxy); - } - - - private void toggleMediaControlsVisiblity() { - if (mMediaController.isShowing()) { - mMediaController.hide(); - } else { - mMediaController.show(); - } - } - - @Override - public void onPrepared(MediaPlayer mp) { - super.onPrepared(mp); - - mVideoSurfaceView.setOnTouchListener(this); - // Get the capabilities of the player for this stream - Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, - MediaPlayer.BYPASS_METADATA_FILTER); - if (data != null) { - mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) - || data.getBoolean(Metadata.PAUSE_AVAILABLE); - mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) - || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); - mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) - || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); - } else { - mCanPause = mCanSeekBack = mCanSeekForward = true; - } - - if (getStartWhenPrepared()) { - mPlayer.start(); - // Clear the flag. - setStartWhenPrepared(false); - } - - // mMediaController status depends on the Metadata result, so put it - // after reading the MetaData. - // And make sure mPlayer state is updated before showing the controller. - if (mMediaController != null) { - mMediaController.setEnabled(true); - mMediaController.show(); - } - - if (mProgressView != null) { - mProgressView.setVisibility(View.GONE); - } - - mVideoWidth = mp.getVideoWidth(); - mVideoHeight = mp.getVideoHeight(); - // This will trigger the onMeasure to get the display size right. - mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight); - - } - - @Override - public boolean fullScreenExited() { - return (mLayout == null); - } - - private final WebChromeClient.CustomViewCallback mCallback = - new WebChromeClient.CustomViewCallback() { - @Override - public void onCustomViewHidden() { - // It listens to SurfaceHolder.Callback.SurfaceDestroyed event - // which happens when the video view is detached from its parent - // view. This happens in the WebChromeClient before this method - // is invoked. - mLayout.removeView(getSurfaceView()); - - if (mProgressView != null) { - mLayout.removeView(mProgressView); - mProgressView = null; - } - mLayout = null; - // Re enable plugin views. - mProxy.getWebView().getViewManager().showAll(); - // Don't show the controller after exiting the full screen. - mMediaController = null; - // Continue the inline mode playing if necessary. - mProxy.dispatchOnStopFullScreen(mPlayingWhenDestroyed); - mProxy = null; - } - }; - - @Override - public void enterFullScreenVideoState(int layerId, - HTML5VideoViewProxy proxy, WebViewClassic webView) { - mFullScreenMode = FULLSCREEN_SURFACECREATING; - mCurrentBufferPercentage = 0; - mPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); - mProxy = proxy; - - mVideoSurfaceView.getHolder().addCallback(mSHCallback); - mVideoSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - mVideoSurfaceView.setFocusable(true); - mVideoSurfaceView.setFocusableInTouchMode(true); - mVideoSurfaceView.requestFocus(); - mVideoSurfaceView.setOnKeyListener(mProxy); - // Create a FrameLayout that will contain the VideoView and the - // progress view (if any). - mLayout = new FrameLayout(mProxy.getContext()); - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - Gravity.CENTER); - - mLayout.addView(getSurfaceView(), layoutParams); - - mLayout.setVisibility(View.VISIBLE); - WebChromeClient client = webView.getWebChromeClient(); - if (client != null) { - if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onShowCustomView"); - client.onShowCustomView(mLayout, mCallback); - // Plugins like Flash will draw over the video so hide - // them while we're playing. - if (webView.getViewManager() != null) - webView.getViewManager().hideAll(); - - if (DebugFlags.TRACE_CALLBACK) { - Log.d(CallbackProxy.LOGTAG, "getVideoLoadingProgressView"); - } - mProgressView = client.getVideoLoadingProgressView(); - if (mProgressView != null) { - mLayout.addView(mProgressView, layoutParams); - mProgressView.setVisibility(View.VISIBLE); - } - } - } - - /** - * @return true when we are in full screen mode, even the surface not fully - * created. - */ - @Override - public boolean isFullScreenMode() { - return true; - } - - // MediaController FUNCTIONS: - @Override - public boolean canPause() { - return mCanPause; - } - - @Override - public boolean canSeekBackward() { - return mCanSeekBack; - } - - @Override - public boolean canSeekForward() { - return mCanSeekForward; - } - - @Override - public int getBufferPercentage() { - if (mPlayer != null) { - return mCurrentBufferPercentage; - } - return 0; - } - - @Override - public int getAudioSessionId() { - if (mPlayer == null) { - return 0; - } - return mPlayer.getAudioSessionId(); - } - - @Override - public void showControllerInFullScreen() { - if (mMediaController != null) { - mMediaController.show(0); - } - } - - // Other listeners functions: - private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = - new MediaPlayer.OnBufferingUpdateListener() { - @Override - public void onBufferingUpdate(MediaPlayer mp, int percent) { - mCurrentBufferPercentage = percent; - } - }; - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (mFullScreenMode >= FULLSCREEN_SURFACECREATED - && mMediaController != null) { - toggleMediaControlsVisiblity(); - } - return false; - } - - @Override - protected void switchProgressView(boolean playerBuffering) { - if (mProgressView != null) { - if (playerBuffering) { - mProgressView.setVisibility(View.VISIBLE); - } else { - mProgressView.setVisibility(View.GONE); - } - } - return; - } - - static class FullScreenMediaController extends MediaController { - - View mVideoView; - - public FullScreenMediaController(Context context, View video) { - super(context); - mVideoView = video; - } - - @Override - public void show() { - super.show(); - if (mVideoView != null) { - mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } - } - - @Override - public void hide() { - if (mVideoView != null) { - mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); - } - super.hide(); - } - - } - -} diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java deleted file mode 100644 index 2ab2ab9..0000000 --- a/core/java/android/webkit/HTML5VideoInline.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.Manifest.permission; -import android.content.pm.PackageManager; -import android.graphics.SurfaceTexture; -import android.webkit.HTML5VideoView; -import android.webkit.HTML5VideoViewProxy; -import android.view.Surface; -import android.opengl.GLES20; -import android.os.PowerManager; - -/** - * @hide This is only used by the browser - */ -public class HTML5VideoInline extends HTML5VideoView{ - - // Due to the fact that the decoder consume a lot of memory, we make the - // surface texture as singleton. But the GL texture (m_textureNames) - // associated with the surface texture can be used for showing the screen - // shot when paused, so they are not singleton. - private static SurfaceTexture mSurfaceTexture = null; - private static int[] mTextureNames = null; - // Every time when the VideoLayer Id change, we need to recreate the - // SurfaceTexture in order to delete the old video's decoder memory. - private static int mVideoLayerUsingSurfaceTexture = -1; - - // Video control FUNCTIONS: - @Override - public void start() { - if (!getPauseDuringPreparing()) { - super.start(); - } - } - - HTML5VideoInline(int videoLayerId, int position, boolean skipPrepare) { - init(videoLayerId, position, skipPrepare); - } - - @Override - public void decideDisplayMode() { - SurfaceTexture surfaceTexture = getSurfaceTexture(getVideoLayerId()); - Surface surface = new Surface(surfaceTexture); - mPlayer.setSurface(surface); - surface.release(); - } - - // Normally called immediately after setVideoURI. But for full screen, - // this should be after surface holder created - @Override - public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { - super.prepareDataAndDisplayMode(proxy); - setFrameAvailableListener(proxy); - // TODO: This is a workaround, after b/5375681 fixed, we should switch - // to the better way. - if (mProxy.getContext().checkCallingOrSelfPermission(permission.WAKE_LOCK) - == PackageManager.PERMISSION_GRANTED) { - mPlayer.setWakeMode(proxy.getContext(), PowerManager.FULL_WAKE_LOCK); - } - } - - // Pause the play and update the play/pause button - @Override - public void pauseAndDispatch(HTML5VideoViewProxy proxy) { - super.pauseAndDispatch(proxy); - } - - // Inline Video specific FUNCTIONS: - - public static SurfaceTexture getSurfaceTexture(int videoLayerId) { - // Create the surface texture. - if (videoLayerId != mVideoLayerUsingSurfaceTexture - || mSurfaceTexture == null - || mTextureNames == null) { - // The GL texture will store in the VideoLayerManager at native side. - // They will be clean up when requested. - // The reason we recreated GL texture name is for screen shot support. - mTextureNames = new int[1]; - GLES20.glGenTextures(1, mTextureNames, 0); - mSurfaceTexture = new SurfaceTexture(mTextureNames[0]); - } - mVideoLayerUsingSurfaceTexture = videoLayerId; - return mSurfaceTexture; - } - - public static boolean surfaceTextureDeleted() { - return (mSurfaceTexture == null); - } - - @Override - public void deleteSurfaceTexture() { - cleanupSurfaceTexture(); - return; - } - - public static void cleanupSurfaceTexture() { - mSurfaceTexture = null; - mVideoLayerUsingSurfaceTexture = -1; - return; - } - - @Override - public int getTextureName() { - if (mTextureNames != null) { - return mTextureNames[0]; - } else { - return 0; - } - } - - private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) { - if (mSurfaceTexture != null) { - mSurfaceTexture.setOnFrameAvailableListener(l); - } - } - -} diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java deleted file mode 100644 index 0e8a5db..0000000 --- a/core/java/android/webkit/HTML5VideoView.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.media.MediaPlayer; -import android.net.Uri; -import android.webkit.HTML5VideoViewProxy; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; - -/** - * @hide This is only used by the browser - */ -public class HTML5VideoView implements MediaPlayer.OnPreparedListener { - - protected static final String LOGTAG = "HTML5VideoView"; - - protected static final String COOKIE = "Cookie"; - protected static final String HIDE_URL_LOGS = "x-hide-urls-from-log"; - - // For handling the seekTo before prepared, we need to know whether or not - // the video is prepared. Therefore, we differentiate the state between - // prepared and not prepared. - // When the video is not prepared, we will have to save the seekTo time, - // and use it when prepared to play. - // NOTE: these values are in sync with VideoLayerAndroid.h in webkit side. - // Please keep them in sync when changed. - static final int STATE_INITIALIZED = 0; - static final int STATE_PREPARING = 1; - static final int STATE_PREPARED = 2; - static final int STATE_PLAYING = 3; - static final int STATE_RESETTED = 4; - static final int STATE_RELEASED = 5; - - protected HTML5VideoViewProxy mProxy; - - // Save the seek time when not prepared. This can happen when switching - // video besides initial load. - protected int mSaveSeekTime; - - // This is used to find the VideoLayer on the native side. - protected int mVideoLayerId; - - // Given the fact we only have one SurfaceTexture, we cannot support multiple - // player at the same time. We may recreate a new one and abandon the old - // one at transition time. - protected static MediaPlayer mPlayer = null; - protected static int mCurrentState = -1; - - // We need to save such info. - protected Uri mUri; - protected Map<String, String> mHeaders; - - // The timer for timeupate events. - // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate - protected static Timer mTimer; - - protected boolean mPauseDuringPreparing; - - // The spec says the timer should fire every 250 ms or less. - private static final int TIMEUPDATE_PERIOD = 250; // ms - private boolean mSkipPrepare = false; - - // common Video control FUNCTIONS: - public void start() { - if (mCurrentState == STATE_PREPARED) { - // When replaying the same video, there is no onPrepared call. - // Therefore, the timer should be set up here. - if (mTimer == null) - { - mTimer = new Timer(); - mTimer.schedule(new TimeupdateTask(mProxy), TIMEUPDATE_PERIOD, - TIMEUPDATE_PERIOD); - } - mPlayer.start(); - setPlayerBuffering(false); - } - } - - public void pause() { - if (isPlaying()) { - mPlayer.pause(); - } else if (mCurrentState == STATE_PREPARING) { - mPauseDuringPreparing = true; - } - // Delete the Timer to stop it since there is no stop call. - if (mTimer != null) { - mTimer.purge(); - mTimer.cancel(); - mTimer = null; - } - } - - public int getDuration() { - if (mCurrentState == STATE_PREPARED) { - return mPlayer.getDuration(); - } else { - return -1; - } - } - - public int getCurrentPosition() { - if (mCurrentState == STATE_PREPARED) { - return mPlayer.getCurrentPosition(); - } - return 0; - } - - public void seekTo(int pos) { - if (mCurrentState == STATE_PREPARED) - mPlayer.seekTo(pos); - else - mSaveSeekTime = pos; - } - - public boolean isPlaying() { - if (mCurrentState == STATE_PREPARED) { - return mPlayer.isPlaying(); - } else { - return false; - } - } - - public void reset() { - if (mCurrentState < STATE_RESETTED) { - mPlayer.reset(); - } - mCurrentState = STATE_RESETTED; - } - - public void stopPlayback() { - if (mCurrentState == STATE_PREPARED) { - mPlayer.stop(); - } - } - - public static void release() { - if (mPlayer != null && mCurrentState != STATE_RELEASED) { - mPlayer.release(); - mPlayer = null; - } - mCurrentState = STATE_RELEASED; - } - - public boolean isReleased() { - return mCurrentState == STATE_RELEASED; - } - - public boolean getPauseDuringPreparing() { - return mPauseDuringPreparing; - } - - // Every time we start a new Video, we create a VideoView and a MediaPlayer - public void init(int videoLayerId, int position, boolean skipPrepare) { - if (mPlayer == null) { - mPlayer = new MediaPlayer(); - mCurrentState = STATE_INITIALIZED; - } - mSkipPrepare = skipPrepare; - // If we want to skip the prepare, then we keep the state. - if (!mSkipPrepare) { - mCurrentState = STATE_INITIALIZED; - } - mProxy = null; - mVideoLayerId = videoLayerId; - mSaveSeekTime = position; - mTimer = null; - mPauseDuringPreparing = false; - } - - protected HTML5VideoView() { - } - - protected static Map<String, String> generateHeaders(String url, - HTML5VideoViewProxy proxy) { - boolean isPrivate = proxy.getWebView().isPrivateBrowsingEnabled(); - String cookieValue = CookieManager.getInstance().getCookie(url, isPrivate); - Map<String, String> headers = new HashMap<String, String>(); - if (cookieValue != null) { - headers.put(COOKIE, cookieValue); - } - if (isPrivate) { - headers.put(HIDE_URL_LOGS, "true"); - } - - return headers; - } - - public void setVideoURI(String uri, HTML5VideoViewProxy proxy) { - // When switching players, surface texture will be reused. - mUri = Uri.parse(uri); - mHeaders = generateHeaders(uri, proxy); - } - - // Listeners setup FUNCTIONS: - public void setOnCompletionListener(HTML5VideoViewProxy proxy) { - mPlayer.setOnCompletionListener(proxy); - } - - public void setOnErrorListener(HTML5VideoViewProxy proxy) { - mPlayer.setOnErrorListener(proxy); - } - - public void setOnPreparedListener(HTML5VideoViewProxy proxy) { - mProxy = proxy; - mPlayer.setOnPreparedListener(this); - } - - public void setOnInfoListener(HTML5VideoViewProxy proxy) { - mPlayer.setOnInfoListener(proxy); - } - - public void prepareDataCommon(HTML5VideoViewProxy proxy) { - if (!mSkipPrepare) { - try { - mPlayer.reset(); - mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders); - mPlayer.prepareAsync(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalStateException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - mCurrentState = STATE_PREPARING; - } else { - // If we skip prepare and the onPrepared happened in inline mode, we - // don't need to call prepare again, we just need to call onPrepared - // to refresh the state here. - if (mCurrentState >= STATE_PREPARED) { - onPrepared(mPlayer); - } - mSkipPrepare = false; - } - } - - public void reprepareData(HTML5VideoViewProxy proxy) { - mPlayer.reset(); - prepareDataCommon(proxy); - } - - // Normally called immediately after setVideoURI. But for full screen, - // this should be after surface holder created - public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { - // SurfaceTexture will be created lazily here for inline mode - decideDisplayMode(); - - setOnCompletionListener(proxy); - setOnPreparedListener(proxy); - setOnErrorListener(proxy); - setOnInfoListener(proxy); - - prepareDataCommon(proxy); - } - - - // Common code - public int getVideoLayerId() { - return mVideoLayerId; - } - - - public int getCurrentState() { - if (isPlaying()) { - return STATE_PLAYING; - } else { - return mCurrentState; - } - } - - private static final class TimeupdateTask extends TimerTask { - private HTML5VideoViewProxy mProxy; - - public TimeupdateTask(HTML5VideoViewProxy proxy) { - mProxy = proxy; - } - - @Override - public void run() { - mProxy.onTimeupdate(); - } - } - - @Override - public void onPrepared(MediaPlayer mp) { - mCurrentState = STATE_PREPARED; - seekTo(mSaveSeekTime); - if (mProxy != null) { - mProxy.onPrepared(mp); - } - if (mPauseDuringPreparing) { - pauseAndDispatch(mProxy); - mPauseDuringPreparing = false; - } - } - - // Pause the play and update the play/pause button - public void pauseAndDispatch(HTML5VideoViewProxy proxy) { - pause(); - if (proxy != null) { - proxy.dispatchOnPaused(); - } - } - - // Below are functions that are different implementation on inline and full- - // screen mode. Some are specific to one type, but currently are called - // directly from the proxy. - public void enterFullScreenVideoState(int layerId, - HTML5VideoViewProxy proxy, WebViewClassic webView) { - } - - public boolean isFullScreenMode() { - return false; - } - - public void decideDisplayMode() { - } - - public boolean getReadyToUseSurfTex() { - return false; - } - - public void deleteSurfaceTexture() { - } - - public int getTextureName() { - return 0; - } - - // This is true only when the player is buffering and paused - public boolean mPlayerBuffering = false; - - public boolean getPlayerBuffering() { - return mPlayerBuffering; - } - - public void setPlayerBuffering(boolean playerBuffering) { - mPlayerBuffering = playerBuffering; - switchProgressView(playerBuffering); - } - - - protected void switchProgressView(boolean playerBuffering) { - // Only used in HTML5VideoFullScreen - } - - public boolean fullScreenExited() { - // Only meaningful for HTML5VideoFullScreen - return false; - } - - private boolean mStartWhenPrepared = false; - - public void setStartWhenPrepared(boolean willPlay) { - mStartWhenPrepared = willPlay; - } - - public boolean getStartWhenPrepared() { - return mStartWhenPrepared; - } - - public void showControllerInFullScreen() { - } - -} diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java deleted file mode 100644 index e8538f6..0000000 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ /dev/null @@ -1,825 +0,0 @@ -/* - * 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 android.webkit; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.SurfaceTexture; -import android.media.MediaPlayer; -import android.net.http.EventHandler; -import android.net.http.Headers; -import android.net.http.RequestHandle; -import android.net.http.RequestQueue; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; - -/** - * <p>Proxy for HTML5 video views. - */ -class HTML5VideoViewProxy extends Handler - implements MediaPlayer.OnPreparedListener, - MediaPlayer.OnCompletionListener, - MediaPlayer.OnErrorListener, - MediaPlayer.OnInfoListener, - SurfaceTexture.OnFrameAvailableListener, - View.OnKeyListener { - // Logging tag. - private static final String LOGTAG = "HTML5VideoViewProxy"; - - // Message Ids for WebCore thread -> UI thread communication. - private static final int PLAY = 100; - private static final int SEEK = 101; - private static final int PAUSE = 102; - private static final int ERROR = 103; - private static final int LOAD_DEFAULT_POSTER = 104; - private static final int BUFFERING_START = 105; - private static final int BUFFERING_END = 106; - private static final int ENTER_FULLSCREEN = 107; - - // Message Ids to be handled on the WebCore thread - private static final int PREPARED = 200; - private static final int ENDED = 201; - private static final int POSTER_FETCHED = 202; - private static final int PAUSED = 203; - private static final int STOPFULLSCREEN = 204; - private static final int RESTORESTATE = 205; - - // Timer thread -> UI thread - private static final int TIMEUPDATE = 300; - - // The C++ MediaPlayerPrivateAndroid object. - int mNativePointer; - // The handler for WebCore thread messages; - private Handler mWebCoreHandler; - // The WebViewClassic instance that created this view. - private WebViewClassic mWebView; - // The poster image to be shown when the video is not playing. - // This ref prevents the bitmap from being GC'ed. - private Bitmap mPoster; - // The poster downloader. - private PosterDownloader mPosterDownloader; - // The seek position. - private int mSeekPosition; - // A helper class to control the playback. This executes on the UI thread! - private static final class VideoPlayer { - // The proxy that is currently playing (if any). - private static HTML5VideoViewProxy mCurrentProxy; - // The VideoView instance. This is a singleton for now, at least until - // http://b/issue?id=1973663 is fixed. - private static HTML5VideoView mHTML5VideoView; - - private static boolean isVideoSelfEnded = false; - - private static void setPlayerBuffering(boolean playerBuffering) { - mHTML5VideoView.setPlayerBuffering(playerBuffering); - } - - // Every time webView setBaseLayer, this will be called. - // When we found the Video layer, then we set the Surface Texture to it. - // Otherwise, we may want to delete the Surface Texture to save memory. - public static void setBaseLayer(int layer) { - // Don't do this for full screen mode. - if (mHTML5VideoView != null - && !mHTML5VideoView.isFullScreenMode() - && !mHTML5VideoView.isReleased()) { - int currentVideoLayerId = mHTML5VideoView.getVideoLayerId(); - SurfaceTexture surfTexture = - HTML5VideoInline.getSurfaceTexture(currentVideoLayerId); - int textureName = mHTML5VideoView.getTextureName(); - - if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) { - int playerState = mHTML5VideoView.getCurrentState(); - if (mHTML5VideoView.getPlayerBuffering()) - playerState = HTML5VideoView.STATE_PREPARING; - boolean foundInTree = nativeSendSurfaceTexture(surfTexture, - layer, currentVideoLayerId, textureName, - playerState); - if (playerState >= HTML5VideoView.STATE_PREPARED - && !foundInTree) { - mHTML5VideoView.pauseAndDispatch(mCurrentProxy); - } - } - } - } - - // When a WebView is paused, we also want to pause the video in it. - public static void pauseAndDispatch() { - if (mHTML5VideoView != null) { - mHTML5VideoView.pauseAndDispatch(mCurrentProxy); - } - } - - public static void enterFullScreenVideo(int layerId, String url, - HTML5VideoViewProxy proxy, WebViewClassic webView) { - // Save the inline video info and inherit it in the full screen - int savePosition = 0; - boolean canSkipPrepare = false; - boolean forceStart = false; - if (mHTML5VideoView != null) { - // We don't allow enter full screen mode while the previous - // full screen video hasn't finished yet. - if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) { - Log.w(LOGTAG, "Try to reenter the full screen mode"); - return; - } - int playerState = mHTML5VideoView.getCurrentState(); - // If we are playing the same video, then it is better to - // save the current position. - if (layerId == mHTML5VideoView.getVideoLayerId()) { - savePosition = mHTML5VideoView.getCurrentPosition(); - canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING - || playerState == HTML5VideoView.STATE_PREPARED - || playerState == HTML5VideoView.STATE_PLAYING) - && !mHTML5VideoView.isFullScreenMode(); - } - if (!canSkipPrepare) { - mHTML5VideoView.reset(); - } else { - forceStart = playerState == HTML5VideoView.STATE_PREPARING - || playerState == HTML5VideoView.STATE_PLAYING; - } - } - mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(), - layerId, savePosition, canSkipPrepare); - mHTML5VideoView.setStartWhenPrepared(forceStart); - mCurrentProxy = proxy; - mHTML5VideoView.setVideoURI(url, mCurrentProxy); - mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView); - } - - public static void exitFullScreenVideo(HTML5VideoViewProxy proxy, - WebViewClassic webView) { - if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) { - WebChromeClient client = webView.getWebChromeClient(); - if (client != null) { - if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView"); - client.onHideCustomView(); - } - } - } - - // This is on the UI thread. - // When native tell Java to play, we need to check whether or not it is - // still the same video by using videoLayerId and treat it differently. - public static void play(String url, int time, HTML5VideoViewProxy proxy, - WebChromeClient client, int videoLayerId) { - int currentVideoLayerId = -1; - boolean backFromFullScreenMode = false; - if (mHTML5VideoView != null) { - currentVideoLayerId = mHTML5VideoView.getVideoLayerId(); - backFromFullScreenMode = mHTML5VideoView.fullScreenExited(); - - // When playing video back to back in full screen mode, - // javascript will switch the src and call play. - // In this case, we can just reuse the same full screen view, - // and play the video after prepared. - if (mHTML5VideoView.isFullScreenMode() - && !backFromFullScreenMode - && currentVideoLayerId != videoLayerId - && mCurrentProxy != proxy) { - mCurrentProxy = proxy; - mHTML5VideoView.setStartWhenPrepared(true); - mHTML5VideoView.setVideoURI(url, proxy); - mHTML5VideoView.reprepareData(proxy); - return; - } - } - - boolean skipPrepare = false; - boolean createInlineView = false; - if (backFromFullScreenMode - && currentVideoLayerId == videoLayerId - && !mHTML5VideoView.isReleased()) { - skipPrepare = true; - createInlineView = true; - } else if(backFromFullScreenMode - || currentVideoLayerId != videoLayerId - || HTML5VideoInline.surfaceTextureDeleted()) { - // Here, we handle the case when switching to a new video, - // either inside a WebView or across WebViews - // For switching videos within a WebView or across the WebView, - // we need to pause the old one and re-create a new media player - // inside the HTML5VideoView. - if (mHTML5VideoView != null) { - if (!backFromFullScreenMode) { - mHTML5VideoView.pauseAndDispatch(mCurrentProxy); - } - mHTML5VideoView.reset(); - } - createInlineView = true; - } - if (createInlineView) { - mCurrentProxy = proxy; - mHTML5VideoView = new HTML5VideoInline(videoLayerId, time, skipPrepare); - - mHTML5VideoView.setVideoURI(url, mCurrentProxy); - mHTML5VideoView.prepareDataAndDisplayMode(proxy); - return; - } - - if (mCurrentProxy == proxy) { - // Here, we handle the case when we keep playing with one video - if (!mHTML5VideoView.isPlaying()) { - mHTML5VideoView.seekTo(time); - mHTML5VideoView.start(); - } - } else if (mCurrentProxy != null) { - // Some other video is already playing. Notify the caller that - // its playback ended. - proxy.dispatchOnEnded(); - } - } - - public static boolean isPlaying(HTML5VideoViewProxy proxy) { - return (mCurrentProxy == proxy && mHTML5VideoView != null - && mHTML5VideoView.isPlaying()); - } - - public static int getCurrentPosition() { - int currentPosMs = 0; - if (mHTML5VideoView != null) { - currentPosMs = mHTML5VideoView.getCurrentPosition(); - } - return currentPosMs; - } - - public static void seek(int time, HTML5VideoViewProxy proxy) { - if (mCurrentProxy == proxy && time >= 0 && mHTML5VideoView != null) { - mHTML5VideoView.seekTo(time); - } - } - - public static void pause(HTML5VideoViewProxy proxy) { - if (mCurrentProxy == proxy && mHTML5VideoView != null) { - mHTML5VideoView.pause(); - } - } - - public static void onPrepared() { - if (!mHTML5VideoView.isFullScreenMode()) { - mHTML5VideoView.start(); - } - } - - public static void end() { - mHTML5VideoView.showControllerInFullScreen(); - if (mCurrentProxy != null) { - if (isVideoSelfEnded) - mCurrentProxy.dispatchOnEnded(); - else - mCurrentProxy.dispatchOnPaused(); - } - isVideoSelfEnded = false; - } - } - - // A bunch event listeners for our VideoView - // MediaPlayer.OnPreparedListener - @Override - public void onPrepared(MediaPlayer mp) { - VideoPlayer.onPrepared(); - Message msg = Message.obtain(mWebCoreHandler, PREPARED); - Map<String, Object> map = new HashMap<String, Object>(); - map.put("dur", new Integer(mp.getDuration())); - map.put("width", new Integer(mp.getVideoWidth())); - map.put("height", new Integer(mp.getVideoHeight())); - msg.obj = map; - mWebCoreHandler.sendMessage(msg); - } - - // MediaPlayer.OnCompletionListener; - @Override - public void onCompletion(MediaPlayer mp) { - // The video ended by itself, so we need to - // send a message to the UI thread to dismiss - // the video view and to return to the WebView. - // arg1 == 1 means the video ends by itself. - sendMessage(obtainMessage(ENDED, 1, 0)); - } - - // MediaPlayer.OnErrorListener - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - sendMessage(obtainMessage(ERROR)); - return false; - } - - public void dispatchOnEnded() { - Message msg = Message.obtain(mWebCoreHandler, ENDED); - mWebCoreHandler.sendMessage(msg); - } - - public void dispatchOnPaused() { - Message msg = Message.obtain(mWebCoreHandler, PAUSED); - mWebCoreHandler.sendMessage(msg); - } - - public void dispatchOnStopFullScreen(boolean stillPlaying) { - Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN); - msg.arg1 = stillPlaying ? 1 : 0; - mWebCoreHandler.sendMessage(msg); - } - - public void dispatchOnRestoreState() { - Message msg = Message.obtain(mWebCoreHandler, RESTORESTATE); - mWebCoreHandler.sendMessage(msg); - } - - public void onTimeupdate() { - sendMessage(obtainMessage(TIMEUPDATE)); - } - - // When there is a frame ready from surface texture, we should tell WebView - // to refresh. - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - // TODO: This should support partial invalidation too. - mWebView.invalidate(); - } - - // Handler for the messages from WebCore or Timer thread to the UI thread. - @Override - public void handleMessage(Message msg) { - // This executes on the UI thread. - switch (msg.what) { - case PLAY: { - String url = (String) msg.obj; - WebChromeClient client = mWebView.getWebChromeClient(); - int videoLayerID = msg.arg1; - if (client != null) { - VideoPlayer.play(url, mSeekPosition, this, client, videoLayerID); - } - break; - } - case ENTER_FULLSCREEN:{ - String url = (String) msg.obj; - WebChromeClient client = mWebView.getWebChromeClient(); - int videoLayerID = msg.arg1; - if (client != null) { - VideoPlayer.enterFullScreenVideo(videoLayerID, url, this, mWebView); - } - break; - } - case SEEK: { - Integer time = (Integer) msg.obj; - mSeekPosition = time; - VideoPlayer.seek(mSeekPosition, this); - break; - } - case PAUSE: { - VideoPlayer.pause(this); - break; - } - case ENDED: - if (msg.arg1 == 1) - VideoPlayer.isVideoSelfEnded = true; - VideoPlayer.end(); - break; - case ERROR: { - WebChromeClient client = mWebView.getWebChromeClient(); - if (client != null) { - if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onHideCustomView"); - client.onHideCustomView(); - } - break; - } - case LOAD_DEFAULT_POSTER: { - WebChromeClient client = mWebView.getWebChromeClient(); - if (client != null) { - if (DebugFlags.TRACE_CALLBACK) { - Log.d(CallbackProxy.LOGTAG, "getDefaultVideoPoster"); - } - doSetPoster(client.getDefaultVideoPoster()); - } - break; - } - case TIMEUPDATE: { - if (VideoPlayer.isPlaying(this)) { - sendTimeupdate(); - } - break; - } - case BUFFERING_START: { - VideoPlayer.setPlayerBuffering(true); - break; - } - case BUFFERING_END: { - VideoPlayer.setPlayerBuffering(false); - break; - } - } - } - - // Everything below this comment executes on the WebCore thread, except for - // the EventHandler methods, which are called on the network thread. - - // A helper class that knows how to download posters - private static final class PosterDownloader implements EventHandler { - // The request queue. This is static as we have one queue for all posters. - private static RequestQueue mRequestQueue; - private static int mQueueRefCount = 0; - // The poster URL - private URL mUrl; - // The proxy we're doing this for. - private final HTML5VideoViewProxy mProxy; - // The poster bytes. We only touch this on the network thread. - private ByteArrayOutputStream mPosterBytes; - // The request handle. We only touch this on the WebCore thread. - private RequestHandle mRequestHandle; - // The response status code. - private int mStatusCode; - // The response headers. - private Headers mHeaders; - // The handler to handle messages on the WebCore thread. - private Handler mHandler; - - public PosterDownloader(String url, HTML5VideoViewProxy proxy) { - try { - mUrl = new URL(url); - } catch (MalformedURLException e) { - mUrl = null; - } - mProxy = proxy; - mHandler = new Handler(); - } - // Start the download. Called on WebCore thread. - public void start() { - retainQueue(); - - if (mUrl == null) { - return; - } - - // Only support downloading posters over http/https. - // FIXME: Add support for other schemes. WebKit seems able to load - // posters over other schemes e.g. file://, but gets the dimensions wrong. - String protocol = mUrl.getProtocol(); - if ("http".equals(protocol) || "https".equals(protocol)) { - mRequestHandle = mRequestQueue.queueRequest(mUrl.toString(), "GET", null, - this, null, 0); - } - } - // Cancel the download if active and release the queue. Called on WebCore thread. - public void cancelAndReleaseQueue() { - if (mRequestHandle != null) { - mRequestHandle.cancel(); - mRequestHandle = null; - } - releaseQueue(); - } - // EventHandler methods. Executed on the network thread. - @Override - public void status(int major_version, - int minor_version, - int code, - String reason_phrase) { - mStatusCode = code; - } - - @Override - public void headers(Headers headers) { - mHeaders = headers; - } - - @Override - public void data(byte[] data, int len) { - if (mPosterBytes == null) { - mPosterBytes = new ByteArrayOutputStream(); - } - mPosterBytes.write(data, 0, len); - } - - @Override - public void endData() { - if (mStatusCode == 200) { - if (mPosterBytes.size() > 0) { - Bitmap poster = BitmapFactory.decodeByteArray( - mPosterBytes.toByteArray(), 0, mPosterBytes.size()); - mProxy.doSetPoster(poster); - } - cleanup(); - } else if (mStatusCode >= 300 && mStatusCode < 400) { - // We have a redirect. - try { - mUrl = new URL(mHeaders.getLocation()); - } catch (MalformedURLException e) { - mUrl = null; - } - if (mUrl != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mRequestHandle != null) { - mRequestHandle.setupRedirect(mUrl.toString(), mStatusCode, - new HashMap<String, String>()); - } - } - }); - } - } - } - - @Override - public void certificate(SslCertificate certificate) { - // Don't care. - } - - @Override - public void error(int id, String description) { - cleanup(); - } - - @Override - public boolean handleSslErrorRequest(SslError error) { - // Don't care. If this happens, data() will never be called so - // mPosterBytes will never be created, so no need to call cleanup. - return false; - } - // Tears down the poster bytes stream. Called on network thread. - private void cleanup() { - if (mPosterBytes != null) { - try { - mPosterBytes.close(); - } catch (IOException ignored) { - // Ignored. - } finally { - mPosterBytes = null; - } - } - } - - // Queue management methods. Called on WebCore thread. - private void retainQueue() { - if (mRequestQueue == null) { - mRequestQueue = new RequestQueue(mProxy.getContext()); - } - mQueueRefCount++; - } - - private void releaseQueue() { - if (mQueueRefCount == 0) { - return; - } - if (--mQueueRefCount == 0) { - mRequestQueue.shutdown(); - mRequestQueue = null; - } - } - } - - /** - * Private constructor. - * @param webView is the WebView that hosts the video. - * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. - */ - private HTML5VideoViewProxy(WebViewClassic webView, int nativePtr) { - // This handler is for the main (UI) thread. - super(Looper.getMainLooper()); - // Save the WebView object. - mWebView = webView; - // Pass Proxy into webview, such that every time we have a setBaseLayer - // call, we tell this Proxy to call the native to update the layer tree - // for the Video Layer's surface texture info - mWebView.setHTML5VideoViewProxy(this); - // Save the native ptr - mNativePointer = nativePtr; - // create the message handler for this thread - createWebCoreHandler(); - } - - private void createWebCoreHandler() { - mWebCoreHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case PREPARED: { - Map<String, Object> map = (Map<String, Object>) msg.obj; - Integer duration = (Integer) map.get("dur"); - Integer width = (Integer) map.get("width"); - Integer height = (Integer) map.get("height"); - nativeOnPrepared(duration.intValue(), width.intValue(), - height.intValue(), mNativePointer); - break; - } - case ENDED: - mSeekPosition = 0; - nativeOnEnded(mNativePointer); - break; - case PAUSED: - nativeOnPaused(mNativePointer); - break; - case POSTER_FETCHED: - Bitmap poster = (Bitmap) msg.obj; - nativeOnPosterFetched(poster, mNativePointer); - break; - case TIMEUPDATE: - nativeOnTimeupdate(msg.arg1, mNativePointer); - break; - case STOPFULLSCREEN: - nativeOnStopFullscreen(msg.arg1, mNativePointer); - break; - case RESTORESTATE: - nativeOnRestoreState(mNativePointer); - break; - } - } - }; - } - - private void doSetPoster(Bitmap poster) { - if (poster == null) { - return; - } - // Save a ref to the bitmap and send it over to the WebCore thread. - mPoster = poster; - Message msg = Message.obtain(mWebCoreHandler, POSTER_FETCHED); - msg.obj = poster; - mWebCoreHandler.sendMessage(msg); - } - - private void sendTimeupdate() { - Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE); - msg.arg1 = VideoPlayer.getCurrentPosition(); - mWebCoreHandler.sendMessage(msg); - } - - public Context getContext() { - return mWebView.getContext(); - } - - // The public methods below are all called from WebKit only. - /** - * Play a video stream. - * @param url is the URL of the video stream. - */ - public void play(String url, int position, int videoLayerID) { - if (url == null) { - return; - } - - if (position > 0) { - seek(position); - } - Message message = obtainMessage(PLAY); - message.arg1 = videoLayerID; - message.obj = url; - sendMessage(message); - } - - /** - * Play a video stream in full screen mode. - * @param url is the URL of the video stream. - */ - public void enterFullscreenForVideoLayer(String url, int videoLayerID) { - if (url == null) { - return; - } - - Message message = obtainMessage(ENTER_FULLSCREEN); - message.arg1 = videoLayerID; - message.obj = url; - sendMessage(message); - } - - /** - * Seek into the video stream. - * @param time is the position in the video stream. - */ - public void seek(int time) { - Message message = obtainMessage(SEEK); - message.obj = new Integer(time); - sendMessage(message); - } - - /** - * Pause the playback. - */ - public void pause() { - Message message = obtainMessage(PAUSE); - sendMessage(message); - } - - /** - * Tear down this proxy object. - */ - public void teardown() { - // This is called by the C++ MediaPlayerPrivate dtor. - // Cancel any active poster download. - if (mPosterDownloader != null) { - mPosterDownloader.cancelAndReleaseQueue(); - } - mNativePointer = 0; - } - - /** - * Load the poster image. - * @param url is the URL of the poster image. - */ - public void loadPoster(String url) { - if (url == null) { - Message message = obtainMessage(LOAD_DEFAULT_POSTER); - sendMessage(message); - return; - } - // Cancel any active poster download. - if (mPosterDownloader != null) { - mPosterDownloader.cancelAndReleaseQueue(); - } - // Load the poster asynchronously - mPosterDownloader = new PosterDownloader(url, this); - mPosterDownloader.start(); - } - - // These three function are called from UI thread only by WebView. - public void setBaseLayer(int layer) { - VideoPlayer.setBaseLayer(layer); - } - - public void pauseAndDispatch() { - VideoPlayer.pauseAndDispatch(); - } - - public void enterFullScreenVideo(int layerId, String url) { - VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView); - } - - public void exitFullScreenVideo() { - VideoPlayer.exitFullScreenVideo(this, mWebView); - } - - /** - * The factory for HTML5VideoViewProxy instances. - * @param webViewCore is the WebViewCore that is requesting the proxy. - * - * @return a new HTML5VideoViewProxy object. - */ - public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) { - return new HTML5VideoViewProxy(webViewCore.getWebViewClassic(), nativePtr); - } - - /* package */ WebViewClassic getWebView() { - return mWebView; - } - - private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); - private native void nativeOnEnded(int nativePointer); - private native void nativeOnPaused(int nativePointer); - private native void nativeOnPosterFetched(Bitmap poster, int nativePointer); - private native void nativeOnTimeupdate(int position, int nativePointer); - private native void nativeOnStopFullscreen(int stillPlaying, int nativePointer); - private native void nativeOnRestoreState(int nativePointer); - private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture, - int baseLayer, int videoLayerId, int textureName, - int playerState); - - @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { - sendMessage(obtainMessage(BUFFERING_START, what, extra)); - } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { - sendMessage(obtainMessage(BUFFERING_END, what, extra)); - } - return false; - } - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) { - VideoPlayer.exitFullScreenVideo(this, mWebView); - return true; - } - } - return false; - } -} diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java deleted file mode 100644 index e6eaa14..0000000 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2006 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 android.webkit; - -import android.net.ProxyProperties; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Set; - -final class JWebCoreJavaBridge extends Handler { - // Identifier for the timer message. - private static final int TIMER_MESSAGE = 1; - // ID for servicing functionptr queue - private static final int FUNCPTR_MESSAGE = 2; - // Log system identifier. - private static final String LOGTAG = "webkit-timers"; - - // Native object pointer for interacting in native code. - private int mNativeBridge; - // Instant timer is used to implement a timer that needs to fire almost - // immediately. - private boolean mHasInstantTimer; - - private boolean mTimerPaused; - private boolean mHasDeferredTimers; - - // keep track of the main WebViewClassic attached to the current window so that we - // can get the proper Context. - private static WeakReference<WebViewClassic> sCurrentMainWebView = - new WeakReference<WebViewClassic>(null); - - /* package */ - static final int REFRESH_PLUGINS = 100; - - private HashMap<String, String> mContentUriToFilePathMap; - - /** - * Construct a new JWebCoreJavaBridge to interface with - * WebCore timers and cookies. - */ - public JWebCoreJavaBridge() { - nativeConstructor(); - - } - - @Override - protected void finalize() { - nativeFinalize(); - } - - static synchronized void setActiveWebView(WebViewClassic webview) { - if (sCurrentMainWebView.get() != null) { - // it is possible if there is a sub-WebView. Do nothing. - return; - } - sCurrentMainWebView = new WeakReference<WebViewClassic>(webview); - } - - static synchronized void removeActiveWebView(WebViewClassic webview) { - if (sCurrentMainWebView.get() != webview) { - // it is possible if there is a sub-WebView. Do nothing. - return; - } - sCurrentMainWebView.clear(); - } - - /** - * Call native timer callbacks. - */ - private void fireSharedTimer() { - // clear the flag so that sharedTimerFired() can set a new timer - mHasInstantTimer = false; - sharedTimerFired(); - } - - /** - * handleMessage - * @param msg The dispatched message. - * - * The only accepted message currently is TIMER_MESSAGE - */ - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case TIMER_MESSAGE: { - if (mTimerPaused) { - mHasDeferredTimers = true; - } else { - fireSharedTimer(); - } - break; - } - case FUNCPTR_MESSAGE: - nativeServiceFuncPtrQueue(); - break; - case REFRESH_PLUGINS: - nativeUpdatePluginDirectories(PluginManager.getInstance(null) - .getPluginDirectories(), ((Boolean) msg.obj) - .booleanValue()); - break; - } - } - - // called from JNI side - private void signalServiceFuncPtrQueue() { - Message msg = obtainMessage(FUNCPTR_MESSAGE); - sendMessage(msg); - } - - private native void nativeServiceFuncPtrQueue(); - - /** - * Pause all timers. - */ - public void pause() { - if (!mTimerPaused) { - mTimerPaused = true; - mHasDeferredTimers = false; - } - } - - /** - * Resume all timers. - */ - public void resume() { - if (mTimerPaused) { - mTimerPaused = false; - if (mHasDeferredTimers) { - mHasDeferredTimers = false; - fireSharedTimer(); - } - } - } - - /** - * Set WebCore cache size. - * @param bytes The cache size in bytes. - */ - public native void setCacheSize(int bytes); - - /** - * Store a cookie string associated with a url. - * @param url The url to be used as a key for the cookie. - * @param value The cookie string to be stored. - */ - private void setCookies(String url, String value) { - if (value.contains("\r") || value.contains("\n")) { - // for security reason, filter out '\r' and '\n' from the cookie - int size = value.length(); - StringBuilder buffer = new StringBuilder(size); - int i = 0; - while (i != -1 && i < size) { - int ir = value.indexOf('\r', i); - int in = value.indexOf('\n', i); - int newi = (ir == -1) ? in : (in == -1 ? ir : (ir < in ? ir - : in)); - if (newi > i) { - buffer.append(value.subSequence(i, newi)); - } else if (newi == -1) { - buffer.append(value.subSequence(i, size)); - break; - } - i = newi + 1; - } - value = buffer.toString(); - } - CookieManager.getInstance().setCookie(url, value); - } - - /** - * Retrieve the cookie string for the given url. - * @param url The resource's url. - * @return A String representing the cookies for the given resource url. - */ - private String cookies(String url) { - return CookieManager.getInstance().getCookie(url); - } - - /** - * Returns whether cookies are enabled or not. - */ - private boolean cookiesEnabled() { - return CookieManager.getInstance().acceptCookie(); - } - - /** - * Returns an array of plugin directoies - */ - private String[] getPluginDirectories() { - return PluginManager.getInstance(null).getPluginDirectories(); - } - - /** - * Returns the path of the plugin data directory - */ - private String getPluginSharedDataDirectory() { - return PluginManager.getInstance(null).getPluginSharedDataDirectory(); - } - - /** - * setSharedTimer - * @param timemillis The relative time when the timer should fire - */ - private void setSharedTimer(long timemillis) { - if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis); - - if (timemillis <= 0) { - // we don't accumulate the sharedTimer unless it is a delayed - // request. This way we won't flood the message queue with - // WebKit messages. This should improve the browser's - // responsiveness to key events. - if (mHasInstantTimer) { - return; - } else { - mHasInstantTimer = true; - Message msg = obtainMessage(TIMER_MESSAGE); - sendMessageDelayed(msg, timemillis); - } - } else { - Message msg = obtainMessage(TIMER_MESSAGE); - sendMessageDelayed(msg, timemillis); - } - } - - /** - * Stop the shared timer. - */ - private void stopSharedTimer() { - if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) { - Log.v(LOGTAG, "stopSharedTimer removing all timers"); - } - removeMessages(TIMER_MESSAGE); - mHasInstantTimer = false; - mHasDeferredTimers = false; - } - - private String[] getKeyStrengthList() { - return CertTool.getKeyStrengthList(); - } - - synchronized private String getSignedPublicKey(int index, String challenge, - String url) { - WebViewClassic current = sCurrentMainWebView.get(); - if (current != null) { - // generateKeyPair expects organizations which we don't have. Ignore - // url. - return CertTool.getSignedPublicKey( - current.getContext(), index, challenge); - } else { - Log.e(LOGTAG, "There is no active WebView for getSignedPublicKey"); - return ""; - } - } - - // Called on the WebCore thread through JNI. - private String resolveFilePathForContentUri(String uri) { - if (mContentUriToFilePathMap != null) { - String fileName = mContentUriToFilePathMap.get(uri); - if (fileName != null) { - return fileName; - } - } - - // Failsafe fallback to just use the last path segment. - // (See OpenableColumns documentation in the SDK) - Uri jUri = Uri.parse(uri); - return jUri.getLastPathSegment(); - } - - public void storeFilePathForContentUri(String path, String contentUri) { - if (mContentUriToFilePathMap == null) { - mContentUriToFilePathMap = new HashMap<String, String>(); - } - mContentUriToFilePathMap.put(contentUri, path); - } - - public void updateProxy(ProxyProperties proxyProperties) { - if (proxyProperties == null) { - nativeUpdateProxy("", ""); - return; - } - - String host = proxyProperties.getHost(); - int port = proxyProperties.getPort(); - if (port != 0) - host += ":" + port; - - nativeUpdateProxy(host, proxyProperties.getExclusionList()); - } - - private native void nativeConstructor(); - private native void nativeFinalize(); - private native void sharedTimerFired(); - private native void nativeUpdatePluginDirectories(String[] directories, - boolean reload); - public native void setNetworkOnLine(boolean online); - public native void setNetworkType(String type, String subtype); - public native void addPackageNames(Set<String> packageNames); - public native void addPackageName(String packageName); - public native void removePackageName(String packageName); - public native void nativeUpdateProxy(String newProxy, String exclusionList); -} diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java deleted file mode 100644 index 01a81c4..0000000 --- a/core/java/android/webkit/JniUtil.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.app.ActivityManager; -import android.content.Context; -import android.net.Uri; -import android.provider.Settings; -import android.util.Log; - -import java.io.File; -import java.io.InputStream; - -class JniUtil { - - static { - System.loadLibrary("webcore"); - System.loadLibrary("chromium_net"); - } - private static final String LOGTAG = "webkit"; - private JniUtil() {} // Utility class, do not instantiate. - - // Used by the Chromium HTTP stack. - private static String sDatabaseDirectory; - private static String sCacheDirectory; - private static Context sContext; - - private static void checkInitialized() { - if (sContext == null) { - throw new IllegalStateException("Call CookieSyncManager::createInstance() or create a webview before using this class"); - } - } - - protected static synchronized void setContext(Context context) { - if (sContext != null) { - return; - } - - sContext = context.getApplicationContext(); - } - - protected static synchronized Context getContext() { - return sContext; - } - - /** - * Called by JNI. Gets the application's database directory, excluding the trailing slash. - * @return String The application's database directory - */ - private static synchronized String getDatabaseDirectory() { - checkInitialized(); - - if (sDatabaseDirectory == null) { - sDatabaseDirectory = sContext.getDatabasePath("dummy").getParent(); - } - - return sDatabaseDirectory; - } - - /** - * Called by JNI. Gets the application's cache directory, excluding the trailing slash. - * @return String The application's cache directory - */ - private static synchronized String getCacheDirectory() { - checkInitialized(); - - if (sCacheDirectory == null) { - File cacheDir = sContext.getCacheDir(); - if (cacheDir == null) { - sCacheDirectory = ""; - } else { - sCacheDirectory = cacheDir.getAbsolutePath(); - } - } - - return sCacheDirectory; - } - - /** - * Called by JNI. Gets the application's package name. - * @return String The application's package name - */ - private static synchronized String getPackageName() { - checkInitialized(); - - return sContext.getPackageName(); - } - - private static final String ANDROID_CONTENT = URLUtil.CONTENT_BASE; - - /** - * Called by JNI. Calculates the size of an input stream by reading it. - * @return long The size of the stream - */ - private static synchronized long contentUrlSize(String url) { - // content:// - if (url.startsWith(ANDROID_CONTENT)) { - try { - // Strip off MIME type. If we don't do this, we can fail to - // load Gmail attachments, because the URL being loaded doesn't - // exactly match the URL we have permission to read. - int mimeIndex = url.lastIndexOf('?'); - if (mimeIndex != -1) { - url = url.substring(0, mimeIndex); - } - Uri uri = Uri.parse(url); - InputStream is = sContext.getContentResolver().openInputStream(uri); - byte[] buffer = new byte[1024]; - int n; - long size = 0; - try { - while ((n = is.read(buffer)) != -1) { - size += n; - } - } finally { - is.close(); - } - return size; - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return 0; - } - } else { - return 0; - } - } - - /** - * Called by JNI. - * - * @return Opened input stream to content - * TODO: Make all content loading use this instead of BrowserFrame.java - */ - private static synchronized InputStream contentUrlStream(String url) { - // content:// - if (url.startsWith(ANDROID_CONTENT)) { - try { - // Strip off mimetype, for compatibility with ContentLoader.java - // (used with Android HTTP stack, now removed). - // If we don't do this, we can fail to load Gmail attachments, - // because the URL being loaded doesn't exactly match the URL we - // have permission to read. - int mimeIndex = url.lastIndexOf('?'); - if (mimeIndex != -1) { - url = url.substring(0, mimeIndex); - } - Uri uri = Uri.parse(url); - return sContext.getContentResolver().openInputStream(uri); - } catch (Exception e) { - Log.e(LOGTAG, "Exception: " + url); - return null; - } - } else { - return null; - } - } - - private static synchronized String getAutofillQueryUrl() { - checkInitialized(); - // If the device has not checked in it won't have pulled down the system setting for the - // Autofill Url. In that case we will not make autofill server requests. - return Settings.Global.getString(sContext.getContentResolver(), - Settings.Global.WEB_AUTOFILL_QUERY_URL); - } - - private static boolean canSatisfyMemoryAllocation(long bytesRequested) { - checkInitialized(); - ActivityManager manager = (ActivityManager) sContext.getSystemService( - Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); - manager.getMemoryInfo(memInfo); - long leftToAllocate = memInfo.availMem - memInfo.threshold; - return !memInfo.lowMemory && bytesRequested < leftToAllocate; - } -} diff --git a/core/java/android/webkit/KeyStoreHandler.java b/core/java/android/webkit/KeyStoreHandler.java deleted file mode 100644 index 849007e..0000000 --- a/core/java/android/webkit/KeyStoreHandler.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2011 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 android.webkit; - -import android.content.Context; -import android.os.Handler; -import android.util.Log; - -/** - * KeyStoreHandler: class responsible for certificate installation to - * the system key store. It reads the certificates file from network - * then pass the bytes to class CertTool. - * This class is only needed if the Chromium HTTP stack is used. - */ -class KeyStoreHandler extends Handler { - private static final String LOGTAG = "KeyStoreHandler"; - - private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(); - - private String mMimeType; - - public KeyStoreHandler(String mimeType) { - mMimeType = mimeType; - } - - /** - * Add data to the internal collection of data. - * @param data A byte array containing the content. - * @param length The length of data. - */ - public void didReceiveData(byte[] data, int length) { - synchronized (mDataBuilder) { - mDataBuilder.append(data, 0, length); - } - } - - public void installCert(Context context) { - String type = CertTool.getCertType(mMimeType); - if (type == null) return; - - // This must be synchronized so that no more data can be added - // after getByteSize returns. - synchronized (mDataBuilder) { - // In the case of downloading certificate, we will save it - // to the KeyStore and stop the current loading so that it - // will not generate a new history page - byte[] cert = new byte[mDataBuilder.getByteSize()]; - int offset = 0; - while (true) { - ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk(); - if (c == null) break; - - if (c.mLength != 0) { - System.arraycopy(c.mArray, 0, cert, offset, c.mLength); - offset += c.mLength; - } - c.release(); - } - CertTool.addCertificate(context, type, cert); - return; - } - } -} diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java deleted file mode 100644 index a1c6a53..0000000 --- a/core/java/android/webkit/L10nUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; - -import java.lang.ref.SoftReference; -import java.util.Map; -import java.util.HashMap; - -/** - * @hide - */ -public class L10nUtils { - - // These array elements must be kept in sync with those defined in - // external/chromium/android/app/l10n_utils.h - private static int[] mIdsArray = { - com.android.internal.R.string.autofill_address_name_separator, // IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR - com.android.internal.R.string.autofill_address_summary_name_format, // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT - com.android.internal.R.string.autofill_address_summary_separator, // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR - com.android.internal.R.string.autofill_address_summary_format, // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT - com.android.internal.R.string.autofill_attention_ignored_re, // IDS_AUTOFILL_ATTENTION_IGNORED_RE - com.android.internal.R.string.autofill_region_ignored_re, // IDS_AUTOFILL_REGION_IGNORED_RE - com.android.internal.R.string.autofill_company_re, // IDS_AUTOFILL_COMPANY_RE - com.android.internal.R.string.autofill_address_line_1_re, // IDS_AUTOFILL_ADDRESS_LINE_1_RE - com.android.internal.R.string.autofill_address_line_1_label_re, // IDS_AUTOFILL_ADDRESS_LINE_1_LABEL_RE - com.android.internal.R.string.autofill_address_line_2_re, // IDS_AUTOFILL_ADDRESS_LINE_2_RE - com.android.internal.R.string.autofill_address_line_3_re, // IDS_AUTOFILL_ADDRESS_LINE_3_RE - com.android.internal.R.string.autofill_country_re, // IDS_AUTOFILL_COUNTRY_RE - com.android.internal.R.string.autofill_zip_code_re, // IDS_AUTOFILL_ZIP_CODE_RE - com.android.internal.R.string.autofill_zip_4_re, // IDS_AUTOFILL_ZIP_4_RE - com.android.internal.R.string.autofill_city_re, // IDS_AUTOFILL_CITY_RE - com.android.internal.R.string.autofill_state_re, // IDS_AUTOFILL_STATE_RE - com.android.internal.R.string.autofill_address_type_same_as_re, // IDS_AUTOFILL_SAME_AS_RE - com.android.internal.R.string.autofill_address_type_use_my_re, // IDS_AUTOFILL_USE_MY_RE - com.android.internal.R.string.autofill_billing_designator_re, // IDS_AUTOFILL_BILLING_DESIGNATOR_RE - com.android.internal.R.string.autofill_shipping_designator_re, // IDS_AUTOFILL_SHIPPING_DESIGNATOR_RE - com.android.internal.R.string.autofill_email_re, // IDS_AUTOFILL_EMAIL_RE - com.android.internal.R.string.autofill_username_re, // IDS_AUTOFILL_USERNAME_RE - com.android.internal.R.string.autofill_name_re, // IDS_AUTOFILL_NAME_RE - com.android.internal.R.string.autofill_name_specific_re, // IDS_AUTOFILL_NAME_SPECIFIC_RE - com.android.internal.R.string.autofill_first_name_re, // IDS_AUTOFILL_FIRST_NAME_RE - com.android.internal.R.string.autofill_middle_initial_re, // IDS_AUTOFILL_MIDDLE_INITIAL_RE - com.android.internal.R.string.autofill_middle_name_re, // IDS_AUTOFILL_MIDDLE_NAME_RE - com.android.internal.R.string.autofill_last_name_re, // IDS_AUTOFILL_LAST_NAME_RE - com.android.internal.R.string.autofill_phone_re, // IDS_AUTOFILL_PHONE_RE - com.android.internal.R.string.autofill_area_code_re, // IDS_AUTOFILL_AREA_CODE_RE - com.android.internal.R.string.autofill_phone_prefix_re, // IDS_AUTOFILL_PHONE_PREFIX_RE - com.android.internal.R.string.autofill_phone_suffix_re, // IDS_AUTOFILL_PHONE_SUFFIX_RE - com.android.internal.R.string.autofill_phone_extension_re, // IDS_AUTOFILL_PHONE_EXTENSION_RE - com.android.internal.R.string.autofill_name_on_card_re, // IDS_AUTOFILL_NAME_ON_CARD_RE - com.android.internal.R.string.autofill_name_on_card_contextual_re, // IDS_AUTOFILL_NAME_ON_CARD_CONTEXTUAL_RE - com.android.internal.R.string.autofill_card_cvc_re, // IDS_AUTOFILL_CARD_CVC_RE - com.android.internal.R.string.autofill_card_number_re, // IDS_AUTOFILL_CARD_NUMBER_RE - com.android.internal.R.string.autofill_expiration_month_re, // IDS_AUTOFILL_EXPIRATION_MONTH_RE - com.android.internal.R.string.autofill_expiration_date_re, // IDS_AUTOFILL_EXPIRATION_DATE_RE - com.android.internal.R.string.autofill_card_ignored_re, // IDS_AUTOFILL_CARD_IGNORED_RE - com.android.internal.R.string.autofill_fax_re, // IDS_AUTOFILL_FAX_RE - com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE - com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE - com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE - com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE - com.android.internal.R.string.autofill_province, // IDS_AUTOFILL_DIALOG_PROVINCE - com.android.internal.R.string.autofill_postal_code, // IDS_AUTOFILL_DIALOG_POSTAL_CODE - com.android.internal.R.string.autofill_state, // IDS_AUTOFILL_DIALOG_STATE - com.android.internal.R.string.autofill_zip_code, // IDS_AUTOFILL_DIALOG_ZIP_CODE - com.android.internal.R.string.autofill_county, // IDS_AUTOFILL_DIALOG_COUNTY - com.android.internal.R.string.autofill_island, // IDS_AUTOFILL_DIALOG_ISLAND - com.android.internal.R.string.autofill_district, // IDS_AUTOFILL_DIALOG_DISTRICT - com.android.internal.R.string.autofill_department, // IDS_AUTOFILL_DIALOG_DEPARTMENT - com.android.internal.R.string.autofill_prefecture, // IDS_AUTOFILL_DIALOG_PREFECTURE - com.android.internal.R.string.autofill_parish, // IDS_AUTOFILL_DIALOG_PARISH - com.android.internal.R.string.autofill_area, // IDS_AUTOFILL_DIALOG_AREA - com.android.internal.R.string.autofill_emirate // IDS_AUTOFILL_DIALOG_EMIRATE - }; - - private static Context mApplicationContext; - private static Map<Integer, SoftReference<String> > mStrings; - - public static void setApplicationContext(Context applicationContext) { - mApplicationContext = applicationContext.getApplicationContext(); - } - - private static String loadString(int id) { - if (mStrings == null) { - mStrings = new HashMap<Integer, SoftReference<String> >(mIdsArray.length); - } - - String localisedString = mApplicationContext.getResources().getString(mIdsArray[id]); - mStrings.put(id, new SoftReference<String>(localisedString)); - return localisedString; - } - - public static String getLocalisedString(int id) { - if (mStrings == null) { - // This is the first time we need a localised string. - // loadString will create the Map. - return loadString(id); - } - - SoftReference<String> ref = mStrings.get(id); - boolean needToLoad = ref == null || ref.get() == null; - return needToLoad ? loadString(id) : ref.get(); - } -} diff --git a/core/java/android/webkit/MockGeolocation.java b/core/java/android/webkit/MockGeolocation.java deleted file mode 100644 index 885c6c2..0000000 --- a/core/java/android/webkit/MockGeolocation.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 android.webkit; - -/** - * Used to configure the mock Geolocation client for the LayoutTests. - * @hide - */ -public final class MockGeolocation { - private WebViewCore mWebViewCore; - - public MockGeolocation(WebViewCore webViewCore) { - mWebViewCore = webViewCore; - } - - /** - * Sets use of the mock Geolocation client. Also resets that client. - */ - public void setUseMock() { - nativeSetUseMock(mWebViewCore); - } - - /** - * Set the position for the mock Geolocation service. - */ - public void setPosition(double latitude, double longitude, double accuracy) { - // This should only ever be called on the WebKit thread. - nativeSetPosition(mWebViewCore, latitude, longitude, accuracy); - } - - /** - * Set the error for the mock Geolocation service. - */ - public void setError(int code, String message) { - // This should only ever be called on the WebKit thread. - nativeSetError(mWebViewCore, code, message); - } - - public void setPermission(boolean allow) { - // This should only ever be called on the WebKit thread. - nativeSetPermission(mWebViewCore, allow); - } - - // Native functions - private static native void nativeSetUseMock(WebViewCore webViewCore); - private static native void nativeSetPosition(WebViewCore webViewCore, double latitude, - double longitude, double accuracy); - private static native void nativeSetError(WebViewCore webViewCore, int code, String message); - private static native void nativeSetPermission(WebViewCore webViewCore, boolean allow); -} diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java deleted file mode 100644 index d91f860..0000000 --- a/core/java/android/webkit/OverScrollGlow.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import com.android.internal.R; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.EdgeEffect; - -/** - * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges. - * @hide - */ -public class OverScrollGlow { - private WebViewClassic mHostView; - - private EdgeEffect mEdgeGlowTop; - private EdgeEffect mEdgeGlowBottom; - private EdgeEffect mEdgeGlowLeft; - private EdgeEffect mEdgeGlowRight; - - private int mOverScrollDeltaX; - private int mOverScrollDeltaY; - - public OverScrollGlow(WebViewClassic host) { - mHostView = host; - Context context = host.getContext(); - mEdgeGlowTop = new EdgeEffect(context); - mEdgeGlowBottom = new EdgeEffect(context); - mEdgeGlowLeft = new EdgeEffect(context); - mEdgeGlowRight = new EdgeEffect(context); - } - - /** - * Pull leftover touch scroll distance into one of the edge glows as appropriate. - * - * @param x Current X scroll offset - * @param y Current Y scroll offset - * @param oldX Old X scroll offset - * @param oldY Old Y scroll offset - * @param maxX Maximum range for horizontal scrolling - * @param maxY Maximum range for vertical scrolling - */ - public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) { - // Only show overscroll bars if there was no movement in any direction - // as a result of scrolling. - if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) { - // Don't show left/right glows if we fit the whole content. - // Also don't show if there was vertical movement. - if (maxX > 0) { - final int pulledToX = oldX + mOverScrollDeltaX; - if (pulledToX < 0) { - mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); - if (!mEdgeGlowRight.isFinished()) { - mEdgeGlowRight.onRelease(); - } - } else if (pulledToX > maxX) { - mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); - if (!mEdgeGlowLeft.isFinished()) { - mEdgeGlowLeft.onRelease(); - } - } - mOverScrollDeltaX = 0; - } - - if (maxY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { - final int pulledToY = oldY + mOverScrollDeltaY; - if (pulledToY < 0) { - mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); - if (!mEdgeGlowBottom.isFinished()) { - mEdgeGlowBottom.onRelease(); - } - } else if (pulledToY > maxY) { - mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); - if (!mEdgeGlowTop.isFinished()) { - mEdgeGlowTop.onRelease(); - } - } - mOverScrollDeltaY = 0; - } - } - } - - /** - * Set touch delta values indicating the current amount of overscroll. - * - * @param deltaX - * @param deltaY - */ - public void setOverScrollDeltas(int deltaX, int deltaY) { - mOverScrollDeltaX = deltaX; - mOverScrollDeltaY = deltaY; - } - - /** - * Absorb leftover fling velocity into one of the edge glows as appropriate. - * - * @param x Current X scroll offset - * @param y Current Y scroll offset - * @param oldX Old X scroll offset - * @param oldY Old Y scroll offset - * @param rangeX Maximum range for horizontal scrolling - * @param rangeY Maximum range for vertical scrolling - */ - public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY) { - if (rangeY > 0 || mHostView.getWebView().getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { - if (y < 0 && oldY >= 0) { - mEdgeGlowTop.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); - if (!mEdgeGlowBottom.isFinished()) { - mEdgeGlowBottom.onRelease(); - } - } else if (y > rangeY && oldY <= rangeY) { - mEdgeGlowBottom.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); - if (!mEdgeGlowTop.isFinished()) { - mEdgeGlowTop.onRelease(); - } - } - } - - if (rangeX > 0) { - if (x < 0 && oldX >= 0) { - mEdgeGlowLeft.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); - if (!mEdgeGlowRight.isFinished()) { - mEdgeGlowRight.onRelease(); - } - } else if (x > rangeX && oldX <= rangeX) { - mEdgeGlowRight.onAbsorb((int) mHostView.mScroller.getCurrVelocity()); - if (!mEdgeGlowLeft.isFinished()) { - mEdgeGlowLeft.onRelease(); - } - } - } - } - - /** - * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null. - * - * @param canvas Canvas to draw into, transformed into view coordinates. - * @return true if glow effects are still animating and the view should invalidate again. - */ - public boolean drawEdgeGlows(Canvas canvas) { - final int scrollX = mHostView.getScrollX(); - final int scrollY = mHostView.getScrollY(); - final int width = mHostView.getWidth(); - int height = mHostView.getHeight(); - - boolean invalidateForGlow = false; - if (!mEdgeGlowTop.isFinished()) { - final int restoreCount = canvas.save(); - - canvas.translate(scrollX, mHostView.getVisibleTitleHeight() + Math.min(0, scrollY)); - mEdgeGlowTop.setSize(width, height); - invalidateForGlow |= mEdgeGlowTop.draw(canvas); - canvas.restoreToCount(restoreCount); - } - if (!mEdgeGlowBottom.isFinished()) { - final int restoreCount = canvas.save(); - - canvas.translate(-width + scrollX, Math.max(mHostView.computeMaxScrollY(), scrollY) - + height); - canvas.rotate(180, width, 0); - mEdgeGlowBottom.setSize(width, height); - invalidateForGlow |= mEdgeGlowBottom.draw(canvas); - canvas.restoreToCount(restoreCount); - } - if (!mEdgeGlowLeft.isFinished()) { - final int restoreCount = canvas.save(); - - canvas.rotate(270); - canvas.translate(-height - scrollY, Math.min(0, scrollX)); - mEdgeGlowLeft.setSize(height, width); - invalidateForGlow |= mEdgeGlowLeft.draw(canvas); - canvas.restoreToCount(restoreCount); - } - if (!mEdgeGlowRight.isFinished()) { - final int restoreCount = canvas.save(); - - canvas.rotate(90); - canvas.translate(scrollY, - -(Math.max(mHostView.computeMaxScrollX(), scrollX) + width)); - mEdgeGlowRight.setSize(height, width); - invalidateForGlow |= mEdgeGlowRight.draw(canvas); - canvas.restoreToCount(restoreCount); - } - return invalidateForGlow; - } - - /** - * @return True if any glow is still animating - */ - public boolean isAnimating() { - return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() || - !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()); - } - - /** - * Release all glows from any touch pulls in progress. - */ - public void releaseAll() { - mEdgeGlowTop.onRelease(); - mEdgeGlowBottom.onRelease(); - mEdgeGlowLeft.onRelease(); - mEdgeGlowRight.onRelease(); - } -} diff --git a/core/java/android/webkit/PluginData.java b/core/java/android/webkit/PluginData.java deleted file mode 100644 index 88fc9b7..0000000 --- a/core/java/android/webkit/PluginData.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 android.webkit; - -import java.io.InputStream; -import java.util.Map; - -/** - * This class encapsulates the content generated by a plugin. The - * data itself is meant to be loaded into webkit via the - * PluginContentLoader class, which needs to be able to construct an - * HTTP response. For this, it needs a stream with the response body, - * the length of the body, the response headers, and the response - * status code. The PluginData class is the container for all these - * parts. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ -@Deprecated -public final class PluginData { - /** - * The content stream. - */ - private InputStream mStream; - /** - * The content length. - */ - private long mContentLength; - /** - * The associated HTTP response headers stored as a map of - * lowercase header name to [ unmodified header name, header value]. - * TODO: This design was always a hack. Remove (involves updating - * the Gears C++ side). - */ - private Map<String, String[]> mHeaders; - - /** - * The associated HTTP response code. - */ - private int mStatusCode; - - /** - * Creates a PluginData instance. - * - * @param stream The stream that supplies content for the plugin. - * @param length The length of the plugin content. - * @param headers The response headers. Map of - * lowercase header name to [ unmodified header name, header value] - * @param length The HTTP response status code. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public PluginData( - InputStream stream, - long length, - Map<String, String[]> headers, - int code) { - mStream = stream; - mContentLength = length; - mHeaders = headers; - mStatusCode = code; - } - - /** - * Returns the input stream that contains the plugin content. - * - * @return An InputStream instance with the plugin content. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public InputStream getInputStream() { - return mStream; - } - - /** - * Returns the length of the plugin content. - * - * @return the length of the plugin content. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public long getContentLength() { - return mContentLength; - } - - /** - * Returns the HTTP response headers associated with the plugin - * content. - * - * @return A Map<String, String[]> containing all headers. The - * mapping is 'lowercase header name' to ['unmodified header - * name', header value]. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public Map<String, String[]> getHeaders() { - return mHeaders; - } - - /** - * Returns the HTTP status code for the response. - * - * @return The HTTP statue code, e.g 200. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public int getStatusCode() { - return mStatusCode; - } -} diff --git a/core/java/android/webkit/PluginFullScreenHolder.java b/core/java/android/webkit/PluginFullScreenHolder.java deleted file mode 100644 index 665cd9d..0000000 --- a/core/java/android/webkit/PluginFullScreenHolder.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2009, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package android.webkit; - -import android.content.Context; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -class PluginFullScreenHolder { - - private final WebViewClassic mWebView; - private final int mNpp; - private final int mOrientation; - - // The container for the plugin view - private static CustomFrameLayout mLayout; - - private View mContentView; - - PluginFullScreenHolder(WebViewClassic webView, int orientation, int npp) { - mWebView = webView; - mNpp = npp; - mOrientation = orientation; - } - - public void setContentView(View contentView) { - - // Create a FrameLayout that will contain the plugin's view - mLayout = new CustomFrameLayout(mWebView.getContext()); - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - Gravity.CENTER); - - mLayout.addView(contentView, layoutParams); - mLayout.setVisibility(View.VISIBLE); - - // fixed size is only used either during pinch zoom or surface is too - // big. Make sure it is not fixed size before setting it to the full - // screen content view. The SurfaceView will be set to the correct mode - // by the ViewManager when it is re-attached to the WebView. - if (contentView instanceof SurfaceView) { - final SurfaceView sView = (SurfaceView) contentView; - if (sView.isFixedSize()) { - sView.getHolder().setSizeFromLayout(); - } - } - - mContentView = contentView; - } - - public void show() { - // Other plugins may attempt to draw so hide them while we're active. - if (mWebView.getViewManager() != null) - mWebView.getViewManager().hideAll(); - - WebChromeClient client = mWebView.getWebChromeClient(); - client.onShowCustomView(mLayout, mOrientation, mCallback); - } - - public void hide() { - WebChromeClient client = mWebView.getWebChromeClient(); - client.onHideCustomView(); - } - - private class CustomFrameLayout extends FrameLayout { - - CustomFrameLayout(Context context) { - super(context); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (event.isSystem()) { - return super.onKeyDown(keyCode, event); - } - mWebView.onKeyDown(keyCode, event); - // always return true as we are the handler - return true; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (event.isSystem()) { - return super.onKeyUp(keyCode, event); - } - mWebView.onKeyUp(keyCode, event); - // always return true as we are the handler - return true; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // always return true as we don't want the event to propagate any further - return true; - } - - @Override - public boolean onTrackballEvent(MotionEvent event) { - mWebView.onTrackballEvent(event); - // always return true as we are the handler - return true; - } - } - - private final WebChromeClient.CustomViewCallback mCallback = - new WebChromeClient.CustomViewCallback() { - public void onCustomViewHidden() { - - mWebView.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN) - .sendToTarget(); - - mWebView.getWebViewCore().sendMessage( - WebViewCore.EventHub.HIDE_FULLSCREEN, mNpp, 0); - - mLayout.removeView(mContentView); - mLayout = null; - - // Re enable plugin views. - mWebView.getViewManager().showAll(); - } - }; -} diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java deleted file mode 100644 index fe40156..0000000 --- a/core/java/android/webkit/PluginManager.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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 android.webkit; - -import java.util.ArrayList; -import java.util.List; - -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.Signature; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.SystemProperties; -import android.util.Log; - -/** - * Class for managing the relationship between the {@link WebViewClassic} and installed - * plugins in the system. You can find this class through - * {@link PluginManager#getInstance}. - * - * @hide pending API solidification - */ -public class PluginManager { - - /** - * Service Action: A plugin wishes to be loaded in the WebView must provide - * {@link android.content.IntentFilter IntentFilter} that accepts this - * action in their AndroidManifest.xml. - * <p> - * TODO: we may change this to a new PLUGIN_ACTION if this is going to be - * public. - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; - - /** - * A plugin wishes to be loaded in the WebView must provide this permission - * in their AndroidManifest.xml. - */ - public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN"; - - private static final String LOGTAG = "PluginManager"; - - private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/"; - - private static final String PLUGIN_TYPE = "type"; - private static final String TYPE_NATIVE = "native"; - - private static PluginManager mInstance = null; - - private final Context mContext; - - private ArrayList<PackageInfo> mPackageInfoCache; - - // Only plugin matches one of the signatures in the list can be loaded - // inside the WebView process - private static final String SIGNATURE_1 = "308204c5308203ada003020102020900d7cb412f75f4887e300d06092a864886f70d010105050030819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f7261746564301e170d3039313030313030323331345a170d3337303231363030323331345a30819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f726174656430820120300d06092a864886f70d01010105000382010d0030820108028201010099724f3e05bbd78843794f357776e04b340e13cb1c9ccb3044865180d7d8fec8166c5bbd876da8b80aa71eb6ba3d4d3455c9a8de162d24a25c4c1cd04c9523affd06a279fc8f0d018f242486bdbb2dbfbf6fcb21ed567879091928b876f7ccebc7bccef157366ebe74e33ae1d7e9373091adab8327482154afc0693a549522f8c796dd84d16e24bb221f5dbb809ca56dd2b6e799c5fa06b6d9c5c09ada54ea4c5db1523a9794ed22a3889e5e05b29f8ee0a8d61efe07ae28f65dece2ff7edc5b1416d7c7aad7f0d35e8f4a4b964dbf50ae9aa6d620157770d974131b3e7e3abd6d163d65758e2f0822db9c88598b9db6263d963d13942c91fc5efe34fc1e06e3020103a382010630820102301d0603551d0e041604145af418e419a639e1657db960996364a37ef20d403081d20603551d230481ca3081c780145af418e419a639e1657db960996364a37ef20d40a181a3a481a030819d310b3009060355040613025553311330110603550408130a43616c69666f726e69613111300f0603550407130853616e204a6f736531233021060355040a131a41646f62652053797374656d7320496e636f72706f7261746564311c301a060355040b1313496e666f726d6174696f6e2053797374656d73312330210603550403131a41646f62652053797374656d7320496e636f72706f7261746564820900d7cb412f75f4887e300c0603551d13040530030101ff300d06092a864886f70d0101050500038201010076c2a11fe303359689c2ebc7b2c398eff8c3f9ad545cdbac75df63bf7b5395b6988d1842d6aa1556d595b5692e08224d667a4c9c438f05e74906c53dd8016dde7004068866f01846365efd146e9bfaa48c9ecf657f87b97c757da11f225c4a24177bf2d7188e6cce2a70a1e8a841a14471eb51457398b8a0addd8b6c8c1538ca8f1e40b4d8b960009ea22c188d28924813d2c0b4a4d334b7cf05507e1fcf0a06fe946c7ffc435e173af6fc3e3400643710acc806f830a14788291d46f2feed9fb5c70423ca747ed1572d752894ac1f19f93989766308579393fabb43649aa8806a313b1ab9a50922a44c2467b9062037f2da0d484d9ffd8fe628eeea629ba637"; - - private static final Signature[] SIGNATURES = new Signature[] { - new Signature(SIGNATURE_1) - }; - - private PluginManager(Context context) { - mContext = context; - mPackageInfoCache = new ArrayList<PackageInfo>(); - } - - public static synchronized PluginManager getInstance(Context context) { - if (mInstance == null) { - if (context == null) { - throw new IllegalStateException( - "First call to PluginManager need a valid context."); - } - mInstance = new PluginManager(context.getApplicationContext()); - } - return mInstance; - } - - /** - * Signal the WebCore thread to refresh its list of plugins. Use this if the - * directory contents of one of the plugin directories has been modified and - * needs its changes reflecting. May cause plugin load and/or unload. - * - * @param reloadOpenPages Set to true to reload all open pages. - */ - public void refreshPlugins(boolean reloadOpenPages) { - BrowserFrame.sJavaBridge.obtainMessage( - JWebCoreJavaBridge.REFRESH_PLUGINS, reloadOpenPages) - .sendToTarget(); - } - - String[] getPluginDirectories() { - - ArrayList<String> directories = new ArrayList<String>(); - PackageManager pm = mContext.getPackageManager(); - List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION), - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); - - synchronized(mPackageInfoCache) { - - // clear the list of existing packageInfo objects - mPackageInfoCache.clear(); - - for (ResolveInfo info : plugins) { - - // retrieve the plugin's service information - ServiceInfo serviceInfo = info.serviceInfo; - if (serviceInfo == null) { - Log.w(LOGTAG, "Ignore bad plugin"); - continue; - } - - // retrieve information from the plugin's manifest - PackageInfo pkgInfo; - try { - pkgInfo = pm.getPackageInfo(serviceInfo.packageName, - PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - } catch (NameNotFoundException e) { - Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } - if (pkgInfo == null) { - continue; - } - - /* - * find the location of the plugin's shared library. The default - * is to assume the app is either a user installed app or an - * updated system app. In both of these cases the library is - * stored in the app's data directory. - */ - String directory = pkgInfo.applicationInfo.dataDir + "/lib"; - final int appFlags = pkgInfo.applicationInfo.flags; - final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM | - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - // preloaded system app with no user updates - if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) { - directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName; - } - - // check if the plugin has the required permissions and - // signatures - if (!containsPluginPermissionAndSignatures(pkgInfo)) { - continue; - } - - // determine the type of plugin from the manifest - if (serviceInfo.metaData == null) { - Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined"); - continue; - } - - String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE); - if (!TYPE_NATIVE.equals(pluginType)) { - Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType); - continue; - } - - try { - Class<?> cls = getPluginClass(serviceInfo.packageName, serviceInfo.name); - - //TODO implement any requirements of the plugin class here! - boolean classFound = true; - - if (!classFound) { - Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class."); - continue; - } - - } catch (NameNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName); - continue; - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name); - continue; - } - - // if all checks have passed then make the plugin available - mPackageInfoCache.add(pkgInfo); - directories.add(directory); - } - } - - return directories.toArray(new String[directories.size()]); - } - - /* package */ - boolean containsPluginPermissionAndSignatures(String pluginAPKName) { - PackageManager pm = mContext.getPackageManager(); - - // retrieve information from the plugin's manifest - try { - PackageInfo pkgInfo = pm.getPackageInfo(pluginAPKName, PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - if (pkgInfo != null) { - return containsPluginPermissionAndSignatures(pkgInfo); - } - } catch (NameNotFoundException e) { - Log.w(LOGTAG, "Can't find plugin: " + pluginAPKName); - } - return false; - } - - private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) { - - // check if the plugin has the required permissions - String permissions[] = pkgInfo.requestedPermissions; - if (permissions == null) { - return false; - } - boolean permissionOk = false; - for (String permit : permissions) { - if (PLUGIN_PERMISSION.equals(permit)) { - permissionOk = true; - break; - } - } - if (!permissionOk) { - return false; - } - - // check to ensure the plugin is properly signed - Signature signatures[] = pkgInfo.signatures; - if (signatures == null) { - return false; - } - if (SystemProperties.getBoolean("ro.secure", false)) { - boolean signatureMatch = false; - for (Signature signature : signatures) { - for (int i = 0; i < SIGNATURES.length; i++) { - if (SIGNATURES[i].equals(signature)) { - signatureMatch = true; - break; - } - } - } - if (!signatureMatch) { - return false; - } - } - - return true; - } - - /* package */ - String getPluginsAPKName(String pluginLib) { - - // basic error checking on input params - if (pluginLib == null || pluginLib.length() == 0) { - return null; - } - - // must be synchronized to ensure the consistency of the cache - synchronized(mPackageInfoCache) { - for (PackageInfo pkgInfo : mPackageInfoCache) { - if (pluginLib.contains(pkgInfo.packageName)) { - return pkgInfo.packageName; - } - } - } - - // if no apk was found then return null - return null; - } - - String getPluginSharedDataDirectory() { - return mContext.getDir("plugins", 0).getPath(); - } - - /* package */ - Class<?> getPluginClass(String packageName, String className) - throws NameNotFoundException, ClassNotFoundException { - Context pluginContext = mContext.createPackageContext(packageName, - Context.CONTEXT_INCLUDE_CODE | - Context.CONTEXT_IGNORE_SECURITY); - ClassLoader pluginCL = pluginContext.getClassLoader(); - return pluginCL.loadClass(className); - } -} diff --git a/core/java/android/webkit/QuadF.java b/core/java/android/webkit/QuadF.java deleted file mode 100644 index e9011e3..0000000 --- a/core/java/android/webkit/QuadF.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.graphics.PointF; - -/** - * A quadrilateral, determined by four points, clockwise order. Typically - * p1 is "top-left" and p4 is "bottom-left" following webkit's rectangle-to- - * FloatQuad conversion. - */ -class QuadF { - public PointF p1; - public PointF p2; - public PointF p3; - public PointF p4; - - public QuadF() { - p1 = new PointF(); - p2 = new PointF(); - p3 = new PointF(); - p4 = new PointF(); - } - - public void offset(float dx, float dy) { - p1.offset(dx, dy); - p2.offset(dx, dy); - p3.offset(dx, dy); - p4.offset(dx, dy); - } - - /** - * Determines if the quadrilateral contains the given point. This does - * not work if the quadrilateral is self-intersecting or if any inner - * angle is reflex (greater than 180 degrees). - */ - public boolean containsPoint(float x, float y) { - return isPointInTriangle(x, y, p1, p2, p3) || - isPointInTriangle(x, y, p1, p3, p4); - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder("QuadF("); - s.append(p1.x).append(",").append(p1.y); - s.append(" - "); - s.append(p2.x).append(",").append(p2.y); - s.append(" - "); - s.append(p3.x).append(",").append(p3.y); - s.append(" - "); - s.append(p4.x).append(",").append(p4.y); - s.append(")"); - return s.toString(); - } - - private static boolean isPointInTriangle(float x0, float y0, - PointF r1, PointF r2, PointF r3) { - // Use the barycentric technique - float x13 = r1.x - r3.x; - float y13 = r1.y - r3.y; - float x23 = r2.x - r3.x; - float y23 = r2.y - r3.y; - float x03 = x0 - r3.x; - float y03 = y0 - r3.y; - - float determinant = (y23 * x13) - (x23 * y13); - float lambda1 = ((y23 * x03) - (x23 * y03))/determinant; - float lambda2 = ((x13 * y03) - (y13 * x03))/determinant; - float lambda3 = 1 - lambda1 - lambda2; - return lambda1 >= 0.0f && lambda2 >= 0.0f && lambda3 >= 0.0f; - } -} diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java deleted file mode 100644 index f9f5b03..0000000 --- a/core/java/android/webkit/SelectActionModeCallback.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.app.Activity; -import android.app.SearchManager; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.provider.Browser; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; - -class SelectActionModeCallback implements ActionMode.Callback { - private WebViewClassic mWebView; - private ActionMode mActionMode; - private boolean mIsTextSelected = true; - - void setWebView(WebViewClassic webView) { - mWebView = webView; - } - - void setTextSelected(boolean isTextSelected) { - mIsTextSelected = isTextSelected; - } - - void finish() { - // It is possible that onCreateActionMode was never called, in the case - // where there is no ActionBar, for example. - if (mActionMode != null) { - mActionMode.finish(); - } - } - - // ActionMode.Callback implementation - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, menu); - - final Context context = mWebView.getContext(); - mode.setTitle(context.getString(com.android.internal.R.string.textSelectionCABTitle)); - mode.setTitleOptionalHint(true); - - // If the action mode UI we're running in isn't capable of taking window focus - // the user won't be able to type into the find on page UI. Disable this functionality. - // (Note that this should only happen in floating dialog windows.) - // This can be removed once we can handle multiple focusable windows at a time - // in a better way. - ClipboardManager cm = (ClipboardManager)(context - .getSystemService(Context.CLIPBOARD_SERVICE)); - boolean isFocusable = mode.isUiFocusable(); - boolean isEditable = mWebView.focusCandidateIsEditableText(); - boolean canPaste = isEditable && cm.hasPrimaryClip() && isFocusable; - boolean canFind = !isEditable && isFocusable; - boolean canCut = isEditable && mIsTextSelected && isFocusable; - boolean canCopy = mIsTextSelected; - boolean canWebSearch = mIsTextSelected; - setMenuVisibility(menu, canFind, com.android.internal.R.id.find); - setMenuVisibility(menu, canPaste, com.android.internal.R.id.paste); - setMenuVisibility(menu, canCut, com.android.internal.R.id.cut); - setMenuVisibility(menu, canCopy, com.android.internal.R.id.copy); - setMenuVisibility(menu, canWebSearch, com.android.internal.R.id.websearch); - mActionMode = mode; - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return true; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch(item.getItemId()) { - case android.R.id.cut: - mWebView.cutSelection(); - mode.finish(); - break; - - case android.R.id.copy: - mWebView.copySelection(); - mode.finish(); - break; - - case android.R.id.paste: - mWebView.pasteFromClipboard(); - mode.finish(); - break; - - case com.android.internal.R.id.share: - String selection = mWebView.getSelection(); - Browser.sendString(mWebView.getContext(), selection); - mode.finish(); - break; - - case com.android.internal.R.id.select_all: - mWebView.selectAll(); - break; - - case com.android.internal.R.id.find: - String sel= mWebView.getSelection(); - mode.finish(); - mWebView.showFindDialog(sel, false); - break; - case com.android.internal.R.id.websearch: - mode.finish(); - Intent i = new Intent(Intent.ACTION_WEB_SEARCH); - i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); - i.putExtra(SearchManager.QUERY, mWebView.getSelection()); - if (!(mWebView.getContext() instanceof Activity)) { - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - mWebView.getContext().startActivity(i); - break; - - default: - return false; - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - mWebView.selectionDone(); - } - - private void setMenuVisibility(Menu menu, boolean visible, int resourceId) { - final MenuItem item = menu.findItem(resourceId); - if (item != null) { - item.setVisible(visible); - } - } -} diff --git a/core/java/android/webkit/SslCertLookupTable.java b/core/java/android/webkit/SslCertLookupTable.java deleted file mode 100644 index 98ace4f..0000000 --- a/core/java/android/webkit/SslCertLookupTable.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.os.Bundle; -import android.net.http.SslError; - -import java.net.MalformedURLException; -import java.net.URL; - -/** - * Stores the user's decision of whether to allow or deny an invalid certificate. - * - * This class is not threadsafe. It is used only on the WebCore thread. Also, it - * is used only by the Chromium HTTP stack. - */ -final class SslCertLookupTable { - private static SslCertLookupTable sTable; - // We store the most severe error we're willing to allow for each host. - private final Bundle table; - - public static SslCertLookupTable getInstance() { - if (sTable == null) { - sTable = new SslCertLookupTable(); - } - return sTable; - } - - private SslCertLookupTable() { - table = new Bundle(); - } - - public void setIsAllowed(SslError sslError) { - String host; - try { - host = new URL(sslError.getUrl()).getHost(); - } catch(MalformedURLException e) { - return; - } - table.putInt(host, sslError.getPrimaryError()); - } - - // We allow the decision to be re-used if it's for the same host and is for - // an error of equal or greater severity than this error. - public boolean isAllowed(SslError sslError) { - String host; - try { - host = new URL(sslError.getUrl()).getHost(); - } catch(MalformedURLException e) { - return false; - } - return table.containsKey(host) && sslError.getPrimaryError() <= table.getInt(host); - } - - public void clear() { - table.clear(); - } -} diff --git a/core/java/android/webkit/SslClientCertLookupTable.java b/core/java/android/webkit/SslClientCertLookupTable.java deleted file mode 100644 index c52b7e8..0000000 --- a/core/java/android/webkit/SslClientCertLookupTable.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011 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 android.webkit; - -import java.security.PrivateKey; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * A simple class to store client certificates that user has chosen. - */ -final class SslClientCertLookupTable { - private static SslClientCertLookupTable sTable; - private final Map<String, PrivateKey> privateKeys; - private final Map<String, byte[][]> certificateChains; - private final Set<String> denied; - - public static synchronized SslClientCertLookupTable getInstance() { - if (sTable == null) { - sTable = new SslClientCertLookupTable(); - } - return sTable; - } - - private SslClientCertLookupTable() { - privateKeys = new HashMap<String, PrivateKey>(); - certificateChains = new HashMap<String, byte[][]>(); - denied = new HashSet<String>(); - } - - public void Allow(String host_and_port, PrivateKey privateKey, byte[][] chain) { - privateKeys.put(host_and_port, privateKey); - certificateChains.put(host_and_port, chain); - denied.remove(host_and_port); - } - - public void Deny(String host_and_port) { - privateKeys.remove(host_and_port); - certificateChains.remove(host_and_port); - denied.add(host_and_port); - } - - public boolean IsAllowed(String host_and_port) { - return privateKeys.containsKey(host_and_port); - } - - public boolean IsDenied(String host_and_port) { - return denied.contains(host_and_port); - } - - public PrivateKey PrivateKey(String host_and_port) { - return privateKeys.get(host_and_port); - } - - public byte[][] CertificateChain(String host_and_port) { - return certificateChains.get(host_and_port); - } -} diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java deleted file mode 100644 index 59fc0cb..0000000 --- a/core/java/android/webkit/UrlInterceptHandler.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2008 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 android.webkit; - -import android.webkit.CacheManager.CacheResult; -import android.webkit.PluginData; -import java.util.Map; - -/** - * @hide - * @deprecated This interface was inteded to be used by Gears. Since Gears was - * deprecated, so is this class. - */ -@Deprecated -public interface UrlInterceptHandler { - - /** - * Given an URL, returns the CacheResult which contains the - * surrogate response for the request, or null if the handler is - * not interested. - * - * @param url URL string. - * @param headers The headers associated with the request. May be null. - * @return The CacheResult containing the surrogate response. - * - * @hide - * @deprecated Do not use, this interface is deprecated. - */ - @Deprecated - public CacheResult service(String url, Map<String, String> headers); - - /** - * Given an URL, returns the PluginData which contains the - * surrogate response for the request, or null if the handler is - * not interested. - * - * @param url URL string. - * @param headers The headers associated with the request. May be null. - * @return The PluginData containing the surrogate response. - * - * @hide - * @deprecated Do not use, this interface is deprecated. - */ - @Deprecated - public PluginData getPluginData(String url, Map<String, String> headers); -} diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java deleted file mode 100644 index bdf6747..0000000 --- a/core/java/android/webkit/UrlInterceptRegistry.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2008 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 android.webkit; - -import android.webkit.CacheManager.CacheResult; -import android.webkit.PluginData; -import android.webkit.UrlInterceptHandler; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; - -/** - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ -@Deprecated -public final class UrlInterceptRegistry { - - private final static String LOGTAG = "intercept"; - - private static boolean mDisabled = false; - - private static LinkedList mHandlerList; - - private static synchronized LinkedList getHandlers() { - if(mHandlerList == null) - mHandlerList = new LinkedList<UrlInterceptHandler>(); - return mHandlerList; - } - - /** - * set the flag to control whether url intercept is enabled or disabled - * - * @param disabled true to disable the cache - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized void setUrlInterceptDisabled(boolean disabled) { - mDisabled = disabled; - } - - /** - * get the state of the url intercept, enabled or disabled - * - * @return return if it is disabled - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized boolean urlInterceptDisabled() { - return mDisabled; - } - - /** - * Register a new UrlInterceptHandler. This handler will be called - * before any that were previously registered. - * - * @param handler The new UrlInterceptHandler object - * @return true if the handler was not previously registered. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized boolean registerHandler( - UrlInterceptHandler handler) { - if (!getHandlers().contains(handler)) { - getHandlers().addFirst(handler); - return true; - } else { - return false; - } - } - - /** - * Unregister a previously registered UrlInterceptHandler. - * - * @param handler A previously registered UrlInterceptHandler. - * @return true if the handler was found and removed from the list. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized boolean unregisterHandler( - UrlInterceptHandler handler) { - return getHandlers().remove(handler); - } - - /** - * Given an url, returns the CacheResult of the first - * UrlInterceptHandler interested, or null if none are. - * - * @return A CacheResult containing surrogate content. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized CacheResult getSurrogate( - String url, Map<String, String> headers) { - if (urlInterceptDisabled()) { - return null; - } - Iterator iter = getHandlers().listIterator(); - while (iter.hasNext()) { - UrlInterceptHandler handler = (UrlInterceptHandler) iter.next(); - CacheResult result = handler.service(url, headers); - if (result != null) { - return result; - } - } - return null; - } - - /** - * Given an url, returns the PluginData of the first - * UrlInterceptHandler interested, or null if none are or if - * intercepts are disabled. - * - * @return A PluginData instance containing surrogate content. - * - * @hide - * @deprecated This class was intended to be used by Gears. Since Gears was - * deprecated, so is this class. - */ - @Deprecated - public static synchronized PluginData getPluginData( - String url, Map<String, String> headers) { - if (urlInterceptDisabled()) { - return null; - } - Iterator iter = getHandlers().listIterator(); - while (iter.hasNext()) { - UrlInterceptHandler handler = (UrlInterceptHandler) iter.next(); - PluginData data = handler.getPluginData(url, headers); - if (data != null) { - return data; - } - } - return null; - } -} diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java deleted file mode 100644 index 34065a1..0000000 --- a/core/java/android/webkit/ViewManager.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * 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 android.webkit; - -import android.util.DisplayMetrics; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsoluteLayout; - -import java.util.ArrayList; - -class ViewManager { - private final WebViewClassic mWebView; - private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>(); - private boolean mHidden; - private boolean mReadyToDraw; - private boolean mZoomInProgress = false; - - // Threshold at which a surface is prevented from further increasing in size - private final int MAX_SURFACE_AREA; - // GPU Limit (hard coded for now) - private static final int MAX_SURFACE_DIMENSION = 2048; - - class ChildView { - int x; - int y; - int width; - int height; - View mView; // generic view to show - - ChildView() { - } - - void setBounds(int x, int y, int width, int height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - void attachView(int x, int y, int width, int height) { - if (mView == null) { - return; - } - setBounds(x, y, width, height); - - mWebView.mPrivateHandler.post(new Runnable() { - public void run() { - // This method may be called multiple times. If the view is - // already attached, just set the new LayoutParams, - // otherwise attach the view and add it to the list of - // children. - requestLayout(ChildView.this); - - if (mView.getParent() == null) { - attachViewOnUIThread(); - } - } - }); - } - - private void attachViewOnUIThread() { - mWebView.getWebView().addView(mView); - mChildren.add(this); - if (!mReadyToDraw) { - mView.setVisibility(View.GONE); - } - } - - void removeView() { - if (mView == null) { - return; - } - mWebView.mPrivateHandler.post(new Runnable() { - public void run() { - removeViewOnUIThread(); - } - }); - } - - private void removeViewOnUIThread() { - mWebView.getWebView().removeView(mView); - mChildren.remove(this); - } - } - - ViewManager(WebViewClassic w) { - mWebView = w; - DisplayMetrics metrics = w.getWebView().getResources().getDisplayMetrics(); - int pixelArea = metrics.widthPixels * metrics.heightPixels; - /* set the threshold to be 275% larger than the screen size. The - percentage is simply an estimation and is not based on anything but - basic trial-and-error tests run on multiple devices. - */ - MAX_SURFACE_AREA = (int)(pixelArea * 2.75); - } - - ChildView createView() { - return new ChildView(); - } - - /** - * This should only be called from the UI thread. - */ - private void requestLayout(ChildView v) { - - int width = mWebView.contentToViewDimension(v.width); - int height = mWebView.contentToViewDimension(v.height); - int x = mWebView.contentToViewX(v.x); - int y = mWebView.contentToViewY(v.y); - - AbsoluteLayout.LayoutParams lp; - ViewGroup.LayoutParams layoutParams = v.mView.getLayoutParams(); - - if (layoutParams instanceof AbsoluteLayout.LayoutParams) { - lp = (AbsoluteLayout.LayoutParams) layoutParams; - lp.width = width; - lp.height = height; - lp.x = x; - lp.y = y; - } else { - lp = new AbsoluteLayout.LayoutParams(width, height, x, y); - } - - // apply the layout to the view - v.mView.setLayoutParams(lp); - - if(v.mView instanceof SurfaceView) { - - final SurfaceView sView = (SurfaceView) v.mView; - - if (sView.isFixedSize() && mZoomInProgress) { - /* If we're already fixed, and we're in a zoom, then do nothing - about the size. Just wait until we get called at the end of - the zoom session (with mZoomInProgress false) and we'll - fixup our size then. - */ - return; - } - - /* Compute proportional fixed width/height if necessary. - * - * NOTE: plugins (e.g. Flash) must not explicitly fix the size of - * their surface. The logic below will result in unexpected behavior - * for the plugin if they attempt to fix the size of the surface. - */ - int fixedW = width; - int fixedH = height; - if (fixedW > MAX_SURFACE_DIMENSION || fixedH > MAX_SURFACE_DIMENSION) { - if (v.width > v.height) { - fixedW = MAX_SURFACE_DIMENSION; - fixedH = v.height * MAX_SURFACE_DIMENSION / v.width; - } else { - fixedH = MAX_SURFACE_DIMENSION; - fixedW = v.width * MAX_SURFACE_DIMENSION / v.height; - } - } - if (fixedW * fixedH > MAX_SURFACE_AREA) { - float area = MAX_SURFACE_AREA; - if (v.width > v.height) { - fixedW = (int)Math.sqrt(area * v.width / v.height); - fixedH = v.height * fixedW / v.width; - } else { - fixedH = (int)Math.sqrt(area * v.height / v.width); - fixedW = v.width * fixedH / v.height; - } - } - - if (fixedW != width || fixedH != height) { - // if we get here, either our dimensions or area (or both) - // exeeded our max, so we had to compute fixedW and fixedH - sView.getHolder().setFixedSize(fixedW, fixedH); - } else if (!sView.isFixedSize() && mZoomInProgress) { - // just freeze where we were (view size) until we're done with - // the zoom progress - sView.getHolder().setFixedSize(sView.getWidth(), - sView.getHeight()); - } else if (sView.isFixedSize() && !mZoomInProgress) { - /* The changing of visibility is a hack to get around a bug in - * the framework that causes the surface to revert to the size - * it was prior to being fixed before it redraws using the - * values currently in its layout. - * - * The surface is destroyed when it is set to invisible and then - * recreated at the new dimensions when it is made visible. The - * same destroy/create step occurs without the change in - * visibility, but then exhibits the behavior described in the - * previous paragraph. - */ - if (sView.getVisibility() == View.VISIBLE) { - sView.setVisibility(View.INVISIBLE); - sView.getHolder().setSizeFromLayout(); - // setLayoutParams() only requests the layout. If we set it - // to VISIBLE now, it will use the old dimension to set the - // size. Post a message to ensure that it shows the new size. - mWebView.mPrivateHandler.post(new Runnable() { - public void run() { - sView.setVisibility(View.VISIBLE); - } - }); - } else { - sView.getHolder().setSizeFromLayout(); - } - } - } - } - - void startZoom() { - mZoomInProgress = true; - for (ChildView v : mChildren) { - requestLayout(v); - } - } - - void endZoom() { - mZoomInProgress = false; - for (ChildView v : mChildren) { - requestLayout(v); - } - } - - void scaleAll() { - for (ChildView v : mChildren) { - requestLayout(v); - } - } - - void hideAll() { - if (mHidden) { - return; - } - for (ChildView v : mChildren) { - v.mView.setVisibility(View.GONE); - } - mHidden = true; - } - - void showAll() { - if (!mHidden) { - return; - } - for (ChildView v : mChildren) { - v.mView.setVisibility(View.VISIBLE); - } - mHidden = false; - } - - void postResetStateAll() { - mWebView.mPrivateHandler.post(new Runnable() { - public void run() { - mReadyToDraw = false; - } - }); - } - - void postReadyToDrawAll() { - mWebView.mPrivateHandler.post(new Runnable() { - public void run() { - mReadyToDraw = true; - for (ChildView v : mChildren) { - v.mView.setVisibility(View.VISIBLE); - } - } - }); - } - - ChildView hitTest(int contentX, int contentY) { - if (mHidden) { - return null; - } - for (ChildView v : mChildren) { - if (v.mView.getVisibility() == View.VISIBLE) { - if (contentX >= v.x && contentX < (v.x + v.width) - && contentY >= v.y && contentY < (v.y + v.height)) { - return v; - } - } - } - return null; - } -} diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java deleted file mode 100644 index 1d44b96..0000000 --- a/core/java/android/webkit/ViewStateSerializer.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2011 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 android.webkit; - -import android.graphics.Point; -import android.webkit.WebViewCore.DrawData; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @hide - */ -class ViewStateSerializer { - - private static final int WORKING_STREAM_STORAGE = 16 * 1024; - - // VERSION = 1 was for pictures encoded using a previous copy of libskia - static final int VERSION = 2; - - static boolean serializeViewState(OutputStream stream, DrawData draw) - throws IOException { - int baseLayer = draw.mBaseLayer; - if (baseLayer == 0) { - return false; - } - DataOutputStream dos = new DataOutputStream(stream); - dos.writeInt(VERSION); - dos.writeInt(draw.mContentSize.x); - dos.writeInt(draw.mContentSize.y); - return nativeSerializeViewState(baseLayer, dos, - new byte[WORKING_STREAM_STORAGE]); - } - - static DrawData deserializeViewState(InputStream stream) - throws IOException { - DataInputStream dis = new DataInputStream(stream); - int version = dis.readInt(); - if (version > VERSION) { - throw new IOException("Unexpected version: " + version); - } - int contentWidth = dis.readInt(); - int contentHeight = dis.readInt(); - int baseLayer = nativeDeserializeViewState(version, dis, - new byte[WORKING_STREAM_STORAGE]); - - final WebViewCore.DrawData draw = new WebViewCore.DrawData(); - draw.mViewState = new WebViewCore.ViewState(); - draw.mContentSize = new Point(contentWidth, contentHeight); - draw.mBaseLayer = baseLayer; - stream.close(); - return draw; - } - - public static void dumpLayerHierarchy(int baseLayer, OutputStream out, int level) { - nativeDumpLayerHierarchy(baseLayer, level, out, - new byte[WORKING_STREAM_STORAGE]); - } - - - private static native void nativeDumpLayerHierarchy(int baseLayer, int level, - OutputStream out, byte[] storage); - - private static native boolean nativeSerializeViewState(int baseLayer, - OutputStream stream, byte[] storage); - - // Returns a pointer to the BaseLayer - private static native int nativeDeserializeViewState(int version, - InputStream stream, byte[] storage); - - private ViewStateSerializer() {} -} diff --git a/core/java/android/webkit/WebBackForwardListClassic.java b/core/java/android/webkit/WebBackForwardListClassic.java deleted file mode 100644 index 2a14e6b..0000000 --- a/core/java/android/webkit/WebBackForwardListClassic.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import java.io.Serializable; -import java.util.ArrayList; - -/* package */ class WebBackForwardListClassic extends WebBackForwardList implements Cloneable, - Serializable { - - // Current position in the list. - private int mCurrentIndex; - // ArrayList of WebHistoryItems for maintaining our copy. - private ArrayList<WebHistoryItemClassic> mArray; - // Flag to indicate that the list is invalid - private boolean mClearPending; - // CallbackProxy to issue client callbacks. - private final CallbackProxy mCallbackProxy; - - /*package*/ WebBackForwardListClassic(CallbackProxy proxy) { - mCurrentIndex = -1; - mArray = new ArrayList<WebHistoryItemClassic>(); - mCallbackProxy = proxy; - } - - public synchronized WebHistoryItemClassic getCurrentItem() { - return getItemAtIndex(mCurrentIndex); - } - - public synchronized int getCurrentIndex() { - return mCurrentIndex; - } - - public synchronized WebHistoryItemClassic getItemAtIndex(int index) { - if (index < 0 || index >= getSize()) { - return null; - } - return mArray.get(index); - } - - public synchronized int getSize() { - return mArray.size(); - } - - /** - * Mark the back/forward list as having a pending clear. This is used on the - * UI side to mark the list as being invalid during the clearHistory method. - */ - /*package*/ synchronized void setClearPending() { - mClearPending = true; - } - - /** - * Return the status of the clear flag. This is used on the UI side to - * determine if the list is valid for checking things like canGoBack. - */ - /*package*/ synchronized boolean getClearPending() { - return mClearPending; - } - - /** - * Add a new history item to the list. This will remove all items after the - * current item and append the new item to the end of the list. Called from - * the WebCore thread only. Synchronized because the UI thread may be - * reading the array or the current index. - * @param item A new history item. - */ - /*package*/ synchronized void addHistoryItem(WebHistoryItem item) { - // Update the current position because we are going to add the new item - // in that slot. - ++mCurrentIndex; - // If the current position is not at the end, remove all history items - // after the current item. - final int size = mArray.size(); - final int newPos = mCurrentIndex; - if (newPos != size) { - for (int i = size - 1; i >= newPos; i--) { - final WebHistoryItem h = mArray.remove(i); - } - } - // Add the item to the list. - mArray.add((WebHistoryItemClassic) item); - if (mCallbackProxy != null) { - mCallbackProxy.onNewHistoryItem(item); - } - } - - /** - * Clear the back/forward list. Called from the WebCore thread. - */ - /*package*/ synchronized void close(int nativeFrame) { - // Clear the array first because nativeClose will call addHistoryItem - // with the current item. - mArray.clear(); - mCurrentIndex = -1; - nativeClose(nativeFrame); - // Reset the clear flag - mClearPending = false; - } - - /* Remove the item at the given index. Called by JNI only. */ - private synchronized void removeHistoryItem(int index) { - // XXX: This is a special case. Since the callback is only triggered - // when removing the first item, we can assert that the index is 0. - // This lets us change the current index without having to query the - // native BackForwardList. - if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) { - throw new AssertionError(); - } - final WebHistoryItem h = mArray.remove(index); - // XXX: If we ever add another callback for removing history items at - // any index, this will no longer be valid. - mCurrentIndex--; - } - - public synchronized WebBackForwardListClassic clone() { - WebBackForwardListClassic l = new WebBackForwardListClassic(null); - if (mClearPending) { - // If a clear is pending, return a copy with only the current item. - l.addHistoryItem(getCurrentItem()); - return l; - } - l.mCurrentIndex = mCurrentIndex; - int size = getSize(); - l.mArray = new ArrayList<WebHistoryItemClassic>(size); - for (int i = 0; i < size; i++) { - // Add a copy of each WebHistoryItem - l.mArray.add(mArray.get(i).clone()); - } - return l; - } - - /** - * Set the new history index. - * @param newIndex The new history index. - */ - /*package*/ synchronized void setCurrentIndex(int newIndex) { - mCurrentIndex = newIndex; - if (mCallbackProxy != null) { - mCallbackProxy.onIndexChanged(getItemAtIndex(newIndex), newIndex); - } - } - - /** - * Restore the history index. - */ - /*package*/ static native synchronized void restoreIndex(int nativeFrame, - int index); - - /* Close the native list. */ - private static native void nativeClose(int nativeFrame); -} diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java deleted file mode 100644 index c27bb5f..0000000 --- a/core/java/android/webkit/WebCoreThreadWatchdog.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.webkit.WebViewCore.EventHub; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -// A Runnable that will monitor if the WebCore thread is still -// processing messages by pinging it every so often. It is safe -// to call the public methods of this class from any thread. -class WebCoreThreadWatchdog implements Runnable { - - // A message with this id is sent by the WebCore thread to notify the - // Watchdog that the WebCore thread is still processing messages - // (i.e. everything is OK). - private static final int IS_ALIVE = 100; - - // This message is placed in the Watchdog's queue and removed when we - // receive an IS_ALIVE. If it is ever processed, we consider the - // WebCore thread unresponsive. - private static final int TIMED_OUT = 101; - - // Wait 10s after hearing back from the WebCore thread before checking it's still alive. - private static final int HEARTBEAT_PERIOD = 10 * 1000; - - // If there's no callback from the WebCore thread for 30s, prompt the user the page has - // become unresponsive. - private static final int TIMEOUT_PERIOD = 30 * 1000; - - // After the first timeout, use a shorter period before re-prompting the user. - private static final int SUBSEQUENT_TIMEOUT_PERIOD = 15 * 1000; - - private Handler mWebCoreThreadHandler; - private Handler mHandler; - private boolean mPaused; - - private Set<WebViewClassic> mWebViews; - - private static WebCoreThreadWatchdog sInstance; - - public synchronized static WebCoreThreadWatchdog start(Handler webCoreThreadHandler) { - if (sInstance == null) { - sInstance = new WebCoreThreadWatchdog(webCoreThreadHandler); - new Thread(sInstance, "WebCoreThreadWatchdog").start(); - } - return sInstance; - } - - public synchronized static void registerWebView(WebViewClassic w) { - if (sInstance != null) { - sInstance.addWebView(w); - } - } - - public synchronized static void unregisterWebView(WebViewClassic w) { - if (sInstance != null) { - sInstance.removeWebView(w); - } - } - - public synchronized static void pause() { - if (sInstance != null) { - sInstance.pauseWatchdog(); - } - } - - public synchronized static void resume() { - if (sInstance != null) { - sInstance.resumeWatchdog(); - } - } - - private void addWebView(WebViewClassic w) { - if (mWebViews == null) { - mWebViews = new HashSet<WebViewClassic>(); - } - mWebViews.add(w); - } - - private void removeWebView(WebViewClassic w) { - mWebViews.remove(w); - } - - private WebCoreThreadWatchdog(Handler webCoreThreadHandler) { - mWebCoreThreadHandler = webCoreThreadHandler; - } - - private void pauseWatchdog() { - mPaused = true; - - if (mHandler == null) { - return; - } - - mHandler.removeMessages(TIMED_OUT); - mHandler.removeMessages(IS_ALIVE); - mWebCoreThreadHandler.removeMessages(EventHub.HEARTBEAT); - } - - private void resumeWatchdog() { - if (!mPaused) { - // Do nothing if we get a call to resume without being paused. - // This can happen during the initialisation of the WebView. - return; - } - - mPaused = false; - - if (mHandler == null) { - return; - } - - mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT, - mHandler.obtainMessage(IS_ALIVE)).sendToTarget(); - mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD); - } - - private void createHandler() { - synchronized (WebCoreThreadWatchdog.class) { - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case IS_ALIVE: - synchronized(WebCoreThreadWatchdog.class) { - if (mPaused) { - return; - } - - // The WebCore thread still seems alive. Reset the countdown timer. - removeMessages(TIMED_OUT); - sendMessageDelayed(obtainMessage(TIMED_OUT), TIMEOUT_PERIOD); - mWebCoreThreadHandler.sendMessageDelayed( - mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT, - mHandler.obtainMessage(IS_ALIVE)), - HEARTBEAT_PERIOD); - } - break; - - case TIMED_OUT: - boolean postedDialog = false; - synchronized (WebCoreThreadWatchdog.class) { - Iterator<WebViewClassic> it = mWebViews.iterator(); - // Check each WebView we are aware of and find one that is capable of - // showing the user a prompt dialog. - while (it.hasNext()) { - WebView activeView = it.next().getWebView(); - - if (activeView.getWindowToken() != null && - activeView.getViewRootImpl() != null) { - postedDialog = activeView.post(new PageNotRespondingRunnable( - activeView.getContext(), this)); - - if (postedDialog) { - // We placed the message into the UI thread for an attached - // WebView so we've made our best attempt to display the - // "page not responding" dialog to the user. Although the - // message is in the queue, there is no guarantee when/if - // the runnable will execute. In the case that the runnable - // never executes, the user will need to terminate the - // process manually. - break; - } - } - } - - if (!postedDialog) { - // There's no active webview we can use to show the dialog, so - // wait again. If we never get a usable view, the user will - // never get the chance to terminate the process, and will - // need to do it manually. - sendMessageDelayed(obtainMessage(TIMED_OUT), - SUBSEQUENT_TIMEOUT_PERIOD); - } - } - break; - } - } - }; - } - } - - @Override - public void run() { - Looper.prepare(); - - createHandler(); - - // Send the initial control to WebViewCore and start the timeout timer as long as we aren't - // paused. - synchronized (WebCoreThreadWatchdog.class) { - if (!mPaused) { - mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT, - mHandler.obtainMessage(IS_ALIVE)).sendToTarget(); - mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD); - } - } - - Looper.loop(); - } - - private class PageNotRespondingRunnable implements Runnable { - Context mContext; - private Handler mWatchdogHandler; - - public PageNotRespondingRunnable(Context context, Handler watchdogHandler) { - mContext = context; - mWatchdogHandler = watchdogHandler; - } - - @Override - public void run() { - // This must run on the UI thread as it is displaying an AlertDialog. - assert Looper.getMainLooper().getThread() == Thread.currentThread(); - new AlertDialog.Builder(mContext) - .setMessage(com.android.internal.R.string.webpage_unresponsive) - .setPositiveButton(com.android.internal.R.string.force_close, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // User chose to force close. - Process.killProcess(Process.myPid()); - } - }) - .setNegativeButton(com.android.internal.R.string.wait, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // The user chose to wait. The last HEARTBEAT message - // will still be in the WebCore thread's queue, so all - // we need to do is post another TIMED_OUT so that the - // user will get prompted again if the WebCore thread - // doesn't sort itself out. - mWatchdogHandler.sendMessageDelayed( - mWatchdogHandler.obtainMessage(TIMED_OUT), - SUBSEQUENT_TIMEOUT_PERIOD); - } - }) - .setOnCancelListener( - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mWatchdogHandler.sendMessageDelayed( - mWatchdogHandler.obtainMessage(TIMED_OUT), - SUBSEQUENT_TIMEOUT_PERIOD); - } - }) - .setIconAttribute(android.R.attr.alertDialogIcon) - .show(); - } - } -} diff --git a/core/java/android/webkit/WebHistoryItemClassic.java b/core/java/android/webkit/WebHistoryItemClassic.java deleted file mode 100644 index 1620fbf..0000000 --- a/core/java/android/webkit/WebHistoryItemClassic.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.graphics.Bitmap; - -import java.net.MalformedURLException; -import java.net.URL; - -/* package */ class WebHistoryItemClassic extends WebHistoryItem implements Cloneable { - // Global identifier count. - private static int sNextId = 0; - // Unique identifier. - private final int mId; - // A point to a native WebHistoryItem instance which contains the actual data - private int mNativeBridge; - // The favicon for this item. - private Bitmap mFavicon; - // The pre-flattened data used for saving the state. - private byte[] mFlattenedData; - // The apple-touch-icon url for use when adding the site to the home screen, - // as obtained from a <link> element in the page. - private String mTouchIconUrlFromLink; - // If no <link> is specified, this holds the default location of the - // apple-touch-icon. - private String mTouchIconUrlServerDefault; - // Custom client data that is not flattened or read by native code. - private Object mCustomData; - - /** - * Basic constructor that assigns a unique id to the item. Called by JNI - * only. - */ - private WebHistoryItemClassic(int nativeBridge) { - synchronized (WebHistoryItemClassic.class) { - mId = sNextId++; - } - mNativeBridge = nativeBridge; - nativeRef(mNativeBridge); - } - - protected void finalize() throws Throwable { - if (mNativeBridge != 0) { - nativeUnref(mNativeBridge); - mNativeBridge = 0; - } - } - - /** - * Construct a new WebHistoryItem with initial flattened data. - * @param data The pre-flattened data coming from restoreState. - */ - /*package*/ WebHistoryItemClassic(byte[] data) { - mFlattenedData = data; - synchronized (WebHistoryItemClassic.class) { - mId = sNextId++; - } - } - - /** - * Construct a clone of a WebHistoryItem from the given item. - * @param item The history item to clone. - */ - private WebHistoryItemClassic(WebHistoryItemClassic item) { - mFlattenedData = item.mFlattenedData; - mId = item.mId; - mFavicon = item.mFavicon; - mNativeBridge = item.mNativeBridge; - if (mNativeBridge != 0) { - nativeRef(mNativeBridge); - } - } - - @Deprecated - public int getId() { - return mId; - } - - public String getUrl() { - if (mNativeBridge == 0) return null; - return nativeGetUrl(mNativeBridge); - } - - public String getOriginalUrl() { - if (mNativeBridge == 0) return null; - return nativeGetOriginalUrl(mNativeBridge); - } - - public String getTitle() { - if (mNativeBridge == 0) return null; - return nativeGetTitle(mNativeBridge); - } - - public Bitmap getFavicon() { - if (mFavicon == null && mNativeBridge != 0) { - mFavicon = nativeGetFavicon(mNativeBridge); - } - return mFavicon; - } - - /** - * Return the touch icon url. - * If no touch icon <link> tag was specified, returns - * <host>/apple-touch-icon.png. The DownloadTouchIcon class that - * attempts to retrieve the touch icon will handle the case where - * that file does not exist. An icon set by a <link> tag is always - * used in preference to an icon saved on the server. - * @hide - */ - public String getTouchIconUrl() { - if (mTouchIconUrlFromLink != null) { - return mTouchIconUrlFromLink; - } else if (mTouchIconUrlServerDefault != null) { - return mTouchIconUrlServerDefault; - } - - try { - URL url = new URL(getOriginalUrl()); - mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(), - "/apple-touch-icon.png").toString(); - } catch (MalformedURLException e) { - return null; - } - return mTouchIconUrlServerDefault; - } - - /** - * Return the custom data provided by the client. - * @hide - */ - public Object getCustomData() { - return mCustomData; - } - - /** - * Set the custom data field. - * @param data An Object containing any data the client wishes to associate - * with the item. - * @hide - */ - public void setCustomData(Object data) { - // NOTE: WebHistoryItems are used in multiple threads. However, the - // public facing apis are all getters with the exception of this one - // api. Since this api is exclusive to clients, we don't make any - // promises about thread safety. - mCustomData = data; - } - - /** - * Set the favicon. - * @param icon A Bitmap containing the favicon for this history item. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ void setFavicon(Bitmap icon) { - mFavicon = icon; - } - - /** - * Set the touch icon url. Will not overwrite an icon that has been - * set already from a <link> tag, unless the new icon is precomposed. - * @hide - */ - /*package*/ void setTouchIconUrl(String url, boolean precomposed) { - if (precomposed || mTouchIconUrlFromLink == null) { - mTouchIconUrlFromLink = url; - } - } - - /** - * Get the pre-flattened data. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ byte[] getFlattenedData() { - if (mNativeBridge != 0) { - return nativeGetFlattenedData(mNativeBridge); - } - return mFlattenedData; - } - - /** - * Inflate this item. - * Note: The VM ensures 32-bit atomic read/write operations so we don't have - * to synchronize this method. - */ - /*package*/ void inflate(int nativeFrame) { - mNativeBridge = inflate(nativeFrame, mFlattenedData); - mFlattenedData = null; - } - - public synchronized WebHistoryItemClassic clone() { - return new WebHistoryItemClassic(this); - } - - /* Natively inflate this item, this method is called in the WebCore thread. - */ - private native int inflate(int nativeFrame, byte[] data); - private native void nativeRef(int nptr); - private native void nativeUnref(int nptr); - private native String nativeGetTitle(int nptr); - private native String nativeGetUrl(int nptr); - private native String nativeGetOriginalUrl(int nptr); - private native byte[] nativeGetFlattenedData(int nptr); - private native Bitmap nativeGetFavicon(int nptr); - -} diff --git a/core/java/android/webkit/WebIconDatabaseClassic.java b/core/java/android/webkit/WebIconDatabaseClassic.java deleted file mode 100644 index d6c4c33..0000000 --- a/core/java/android/webkit/WebIconDatabaseClassic.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.ContentResolver; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.os.Handler; -import android.os.Message; -import android.provider.Browser; -import android.util.Log; - -import java.io.File; -import java.util.HashMap; -import java.util.Vector; - -class WebIconDatabaseClassic extends WebIconDatabase { - private static final String LOGTAG = "WebIconDatabase"; - // Global instance of a WebIconDatabase - private static WebIconDatabaseClassic sIconDatabase; - // EventHandler for handling messages before and after the WebCore thread is - // ready. - private final EventHandler mEventHandler = new EventHandler(); - - // Class to handle messages before WebCore is ready - private static class EventHandler extends Handler { - // Message ids - static final int OPEN = 0; - static final int CLOSE = 1; - static final int REMOVE_ALL = 2; - static final int REQUEST_ICON = 3; - static final int RETAIN_ICON = 4; - static final int RELEASE_ICON = 5; - static final int BULK_REQUEST_ICON = 6; - // Message for dispatching icon request results - private static final int ICON_RESULT = 10; - // Actual handler that runs in WebCore thread - private Handler mHandler; - // Vector of messages before the WebCore thread is ready - private Vector<Message> mMessages = new Vector<Message>(); - // Class to handle a result dispatch - private class IconResult { - private final String mUrl; - private final Bitmap mIcon; - private final IconListener mListener; - IconResult(String url, Bitmap icon, IconListener l) { - mUrl = url; - mIcon = icon; - mListener = l; - } - void dispatch() { - mListener.onReceivedIcon(mUrl, mIcon); - } - } - - @Override - public void handleMessage(Message msg) { - // Note: This is the message handler for the UI thread. - switch (msg.what) { - case ICON_RESULT: - ((IconResult) msg.obj).dispatch(); - break; - } - } - - // Called by WebCore thread to create the actual handler - private synchronized void createHandler() { - if (mHandler == null) { - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - // Note: This is the message handler for the WebCore - // thread. - switch (msg.what) { - case OPEN: - nativeOpen((String) msg.obj); - break; - - case CLOSE: - nativeClose(); - break; - - case REMOVE_ALL: - nativeRemoveAllIcons(); - break; - - case REQUEST_ICON: - IconListener l = (IconListener) msg.obj; - String url = msg.getData().getString("url"); - requestIconAndSendResult(url, l); - break; - - case BULK_REQUEST_ICON: - bulkRequestIcons(msg); - break; - - case RETAIN_ICON: - nativeRetainIconForPageUrl((String) msg.obj); - break; - - case RELEASE_ICON: - nativeReleaseIconForPageUrl((String) msg.obj); - break; - } - } - }; - // Transfer all pending messages - for (int size = mMessages.size(); size > 0; size--) { - mHandler.sendMessage(mMessages.remove(0)); - } - mMessages = null; - } - } - - private synchronized boolean hasHandler() { - return mHandler != null; - } - - private synchronized void postMessage(Message msg) { - if (mMessages != null) { - mMessages.add(msg); - } else { - mHandler.sendMessage(msg); - } - } - - private void bulkRequestIcons(Message msg) { - HashMap map = (HashMap) msg.obj; - IconListener listener = (IconListener) map.get("listener"); - ContentResolver cr = (ContentResolver) map.get("contentResolver"); - String where = (String) map.get("where"); - - Cursor c = null; - try { - c = cr.query( - Browser.BOOKMARKS_URI, - new String[] { Browser.BookmarkColumns.URL }, - where, null, null); - if (c.moveToFirst()) { - do { - String url = c.getString(0); - requestIconAndSendResult(url, listener); - } while (c.moveToNext()); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "BulkRequestIcons", e); - } finally { - if (c != null) c.close(); - } - } - - private void requestIconAndSendResult(String url, IconListener listener) { - Bitmap icon = nativeIconForPageUrl(url); - if (icon != null) { - sendMessage(obtainMessage(ICON_RESULT, - new IconResult(url, icon, listener))); - } - } - } - - @Override - public void open(String path) { - if (path != null) { - // Make the directories and parents if they don't exist - File db = new File(path); - if (!db.exists()) { - db.mkdirs(); - } - mEventHandler.postMessage( - Message.obtain(null, EventHandler.OPEN, db.getAbsolutePath())); - } - } - - @Override - public void close() { - mEventHandler.postMessage( - Message.obtain(null, EventHandler.CLOSE)); - } - - @Override - public void removeAllIcons() { - mEventHandler.postMessage( - Message.obtain(null, EventHandler.REMOVE_ALL)); - } - - /** - * Request the Bitmap representing the icon for the given page - * url. If the icon exists, the listener will be called with the result. - * @param url The page's url. - * @param listener An implementation on IconListener to receive the result. - */ - public void requestIconForPageUrl(String url, IconListener listener) { - if (listener == null || url == null) { - return; - } - Message msg = Message.obtain(null, EventHandler.REQUEST_ICON, listener); - msg.getData().putString("url", url); - mEventHandler.postMessage(msg); - } - - /** {@hide} - */ - public void bulkRequestIconForPageUrl(ContentResolver cr, String where, - IconListener listener) { - if (listener == null) { - return; - } - - // Special case situation: we don't want to add this message to the - // queue if there is no handler because we may never have a real - // handler to service the messages and the cursor will never get - // closed. - if (mEventHandler.hasHandler()) { - // Don't use Bundle as it is parcelable. - HashMap<String, Object> map = new HashMap<String, Object>(); - map.put("contentResolver", cr); - map.put("where", where); - map.put("listener", listener); - Message msg = - Message.obtain(null, EventHandler.BULK_REQUEST_ICON, map); - mEventHandler.postMessage(msg); - } - } - - @Override - public void retainIconForPageUrl(String url) { - if (url != null) { - mEventHandler.postMessage( - Message.obtain(null, EventHandler.RETAIN_ICON, url)); - } - } - - @Override - public void releaseIconForPageUrl(String url) { - if (url != null) { - mEventHandler.postMessage( - Message.obtain(null, EventHandler.RELEASE_ICON, url)); - } - } - - /** - * Get the global instance of WebIconDatabase. - * @return A single instance of WebIconDatabase. It will be the same - * instance for the current process each time this method is - * called. - */ - public static WebIconDatabaseClassic getInstance() { - // XXX: Must be created in the UI thread. - if (sIconDatabase == null) { - sIconDatabase = new WebIconDatabaseClassic(); - } - return sIconDatabase; - } - - /** - * Create the internal handler and transfer all pending messages. - * XXX: Called by WebCore thread only! - */ - /*package*/ void createHandler() { - mEventHandler.createHandler(); - } - - /** - * Private constructor to avoid anyone else creating an instance. - */ - private WebIconDatabaseClassic() {} - - // Native functions - private static native void nativeOpen(String path); - private static native void nativeClose(); - private static native void nativeRemoveAllIcons(); - private static native Bitmap nativeIconForPageUrl(String url); - private static native void nativeRetainIconForPageUrl(String url); - private static native void nativeReleaseIconForPageUrl(String url); -} diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java deleted file mode 100644 index c10a429..0000000 --- a/core/java/android/webkit/WebSettingsClassic.java +++ /dev/null @@ -1,1744 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Handler; -import android.os.Message; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.util.EventLog; - -import java.util.Locale; - -/** - * WebSettings implementation for the WebViewClassic implementation of WebView. - * @hide - */ -public class WebSettingsClassic extends WebSettings { - // TODO: Keep this up to date - private static final String PREVIOUS_VERSION = "4.1.1"; - - // WebView associated with this WebSettings. - private WebViewClassic mWebView; - // BrowserFrame used to access the native frame pointer. - private BrowserFrame mBrowserFrame; - // Flag to prevent multiple SYNC messages at one time. - private boolean mSyncPending = false; - // Custom handler that queues messages until the WebCore thread is active. - private final EventHandler mEventHandler; - - // Private settings so we don't have to go into native code to - // retrieve the values. After setXXX, postSync() needs to be called. - // - // The default values need to match those in WebSettings.cpp - // If the defaults change, please also update the JavaDocs so developers - // know what they are. - private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS; - private Context mContext; - private int mTextSize = 100; - private String mStandardFontFamily = "sans-serif"; - private String mFixedFontFamily = "monospace"; - private String mSansSerifFontFamily = "sans-serif"; - private String mSerifFontFamily = "serif"; - private String mCursiveFontFamily = "cursive"; - private String mFantasyFontFamily = "fantasy"; - private String mDefaultTextEncoding; - private String mUserAgent; - private boolean mUseDefaultUserAgent; - private String mAcceptLanguage; - private int mMinimumFontSize = 8; - private int mMinimumLogicalFontSize = 8; - private int mDefaultFontSize = 16; - private int mDefaultFixedFontSize = 13; - private int mPageCacheCapacity = 0; - private boolean mLoadsImagesAutomatically = true; - private boolean mBlockNetworkImage = false; - private boolean mBlockNetworkLoads; - private boolean mJavaScriptEnabled = false; - private boolean mAllowUniversalAccessFromFileURLs = false; - private boolean mAllowFileAccessFromFileURLs = false; - private boolean mHardwareAccelSkia = false; - private boolean mShowVisualIndicator = false; - private PluginState mPluginState = PluginState.OFF; - private boolean mJavaScriptCanOpenWindowsAutomatically = false; - private boolean mUseDoubleTree = false; - private boolean mUseWideViewport = false; - private boolean mSupportMultipleWindows = false; - private boolean mShrinksStandaloneImagesToFit = false; - private long mMaximumDecodedImageSize = 0; // 0 means default - private boolean mPrivateBrowsingEnabled = false; - private boolean mSyntheticLinksEnabled = true; - // HTML5 API flags - private boolean mAppCacheEnabled = false; - private boolean mDatabaseEnabled = false; - private boolean mDomStorageEnabled = false; - private boolean mWorkersEnabled = false; // only affects V8. - private boolean mGeolocationEnabled = true; - private boolean mXSSAuditorEnabled = false; - private boolean mLinkPrefetchEnabled = false; - // HTML5 configuration parameters - private long mAppCacheMaxSize = Long.MAX_VALUE; - private String mAppCachePath = null; - private String mDatabasePath = ""; - // The WebCore DatabaseTracker only allows the database path to be set - // once. Keep track of when the path has been set. - private boolean mDatabasePathHasBeenSet = false; - private String mGeolocationDatabasePath = ""; - // Don't need to synchronize the get/set methods as they - // are basic types, also none of these values are used in - // native WebCore code. - private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM; - private RenderPriority mRenderPriority = RenderPriority.NORMAL; - private int mOverrideCacheMode = LOAD_DEFAULT; - private int mDoubleTapZoom = 100; - private boolean mSaveFormData = true; - private boolean mAutoFillEnabled = false; - private boolean mSavePassword = true; - private boolean mLightTouchEnabled = false; - private boolean mNeedInitialFocus = true; - private boolean mNavDump = false; - private boolean mSupportZoom = true; - private boolean mMediaPlaybackRequiresUserGesture = true; - private boolean mBuiltInZoomControls = false; - private boolean mDisplayZoomControls = true; - private boolean mAllowFileAccess = true; - private boolean mAllowContentAccess = true; - private boolean mLoadWithOverviewMode = false; - private boolean mEnableSmoothTransition = false; - private boolean mForceUserScalable = false; - private boolean mPasswordEchoEnabled = true; - - // AutoFill Profile data - public static class AutoFillProfile { - private int mUniqueId; - private String mFullName; - private String mEmailAddress; - private String mCompanyName; - private String mAddressLine1; - private String mAddressLine2; - private String mCity; - private String mState; - private String mZipCode; - private String mCountry; - private String mPhoneNumber; - - public AutoFillProfile(int uniqueId, String fullName, String email, - String companyName, String addressLine1, String addressLine2, - String city, String state, String zipCode, String country, - String phoneNumber) { - mUniqueId = uniqueId; - mFullName = fullName; - mEmailAddress = email; - mCompanyName = companyName; - mAddressLine1 = addressLine1; - mAddressLine2 = addressLine2; - mCity = city; - mState = state; - mZipCode = zipCode; - mCountry = country; - mPhoneNumber = phoneNumber; - } - - public int getUniqueId() { return mUniqueId; } - public String getFullName() { return mFullName; } - public String getEmailAddress() { return mEmailAddress; } - public String getCompanyName() { return mCompanyName; } - public String getAddressLine1() { return mAddressLine1; } - public String getAddressLine2() { return mAddressLine2; } - public String getCity() { return mCity; } - public String getState() { return mState; } - public String getZipCode() { return mZipCode; } - public String getCountry() { return mCountry; } - public String getPhoneNumber() { return mPhoneNumber; } - } - - - private AutoFillProfile mAutoFillProfile; - - private boolean mUseWebViewBackgroundForOverscroll = true; - - // private WebSettings, not accessible by the host activity - static private int mDoubleTapToastCount = 3; - - private static final String PREF_FILE = "WebViewSettings"; - private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count"; - - // Class to handle messages before WebCore is ready. - private class EventHandler { - // Message id for syncing - static final int SYNC = 0; - // Message id for setting priority - static final int PRIORITY = 1; - // Message id for writing double-tap toast count - static final int SET_DOUBLE_TAP_TOAST_COUNT = 2; - // Actual WebCore thread handler - private Handler mHandler; - - private synchronized void createHandler() { - // as mRenderPriority can be set before thread is running, sync up - setRenderPriority(); - - // create a new handler - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case SYNC: - synchronized (WebSettingsClassic.this) { - if (mBrowserFrame.mNativeFrame != 0) { - nativeSync(mBrowserFrame.mNativeFrame); - } - mSyncPending = false; - } - break; - - case PRIORITY: { - setRenderPriority(); - break; - } - - case SET_DOUBLE_TAP_TOAST_COUNT: { - SharedPreferences.Editor editor = mContext - .getSharedPreferences(PREF_FILE, - Context.MODE_PRIVATE).edit(); - editor.putInt(DOUBLE_TAP_TOAST_COUNT, - mDoubleTapToastCount); - editor.commit(); - break; - } - } - } - }; - } - - private void setRenderPriority() { - synchronized (WebSettingsClassic.this) { - if (mRenderPriority == RenderPriority.NORMAL) { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_DEFAULT); - } else if (mRenderPriority == RenderPriority.HIGH) { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_FOREGROUND + - android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); - } else if (mRenderPriority == RenderPriority.LOW) { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_BACKGROUND); - } - } - } - - /** - * Send a message to the private queue or handler. - */ - private synchronized boolean sendMessage(Message msg) { - if (mHandler != null) { - mHandler.sendMessage(msg); - return true; - } else { - return false; - } - } - } - - // User agent strings. - private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " + - "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " + - "Chrome/11.0.696.34 Safari/534.24"; - private static final String IPHONE_USERAGENT = - "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)" - + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0" - + " Mobile/7A341 Safari/528.16"; - private static Locale sLocale; - private static Object sLockForLocaleSettings; - - /** - * Package constructor to prevent clients from creating a new settings - * instance. - */ - WebSettingsClassic(Context context, WebViewClassic webview) { - mEventHandler = new EventHandler(); - mContext = context; - mWebView = webview; - mDefaultTextEncoding = context.getString(com.android.internal. - R.string.default_text_encoding); - - if (sLockForLocaleSettings == null) { - sLockForLocaleSettings = new Object(); - sLocale = Locale.getDefault(); - } - mAcceptLanguage = getCurrentAcceptLanguage(); - mUserAgent = getCurrentUserAgent(); - mUseDefaultUserAgent = true; - - mBlockNetworkLoads = mContext.checkPermission( - "android.permission.INTERNET", android.os.Process.myPid(), - android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED; - - // SDK specific settings. See issue 6212665 - if (mContext.getApplicationInfo().targetSdkVersion < - Build.VERSION_CODES.JELLY_BEAN) { - mAllowUniversalAccessFromFileURLs = true; - mAllowFileAccessFromFileURLs = true; - } - try { - mPasswordEchoEnabled = - Settings.System.getInt(context.getContentResolver(), - Settings.System.TEXT_SHOW_PASSWORD) != 0; - } catch (SettingNotFoundException e) { - mPasswordEchoEnabled = true; - } - } - - private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US"; - - /** - * Looks at sLocale and returns current AcceptLanguage String. - * @return Current AcceptLanguage String. - */ - private String getCurrentAcceptLanguage() { - Locale locale; - synchronized(sLockForLocaleSettings) { - locale = sLocale; - } - StringBuilder buffer = new StringBuilder(); - addLocaleToHttpAcceptLanguage(buffer, locale); - - if (!Locale.US.equals(locale)) { - if (buffer.length() > 0) { - buffer.append(", "); - } - buffer.append(ACCEPT_LANG_FOR_US_LOCALE); - } - - return buffer.toString(); - } - - /** - * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish, - * to new standard. - */ - private static String convertObsoleteLanguageCodeToNew(String langCode) { - if (langCode == null) { - return null; - } - if ("iw".equals(langCode)) { - // Hebrew - return "he"; - } else if ("in".equals(langCode)) { - // Indonesian - return "id"; - } else if ("ji".equals(langCode)) { - // Yiddish - return "yi"; - } - return langCode; - } - - private static void addLocaleToHttpAcceptLanguage(StringBuilder builder, - Locale locale) { - String language = convertObsoleteLanguageCodeToNew(locale.getLanguage()); - if (language != null) { - builder.append(language); - String country = locale.getCountry(); - if (country != null) { - builder.append("-"); - builder.append(country); - } - } - } - - /** - * Looks at sLocale and mContext and returns current UserAgent String. - * @return Current UserAgent String. - */ - private synchronized String getCurrentUserAgent() { - Locale locale; - synchronized(sLockForLocaleSettings) { - locale = sLocale; - } - return getDefaultUserAgentForLocale(mContext, locale); - } - - /** - * Returns the default User-Agent used by a WebView. - * An instance of WebView could use a different User-Agent if a call - * is made to {@link WebSettings#setUserAgent(int)} or - * {@link WebSettings#setUserAgentString(String)}. - * - * @param context a Context object used to access application assets - * @param locale The Locale to use in the User-Agent string. - * @see WebViewFactoryProvider#getDefaultUserAgent(Context) - * @see WebView#getDefaultUserAgent(Context) - */ - public static String getDefaultUserAgentForLocale(Context context, Locale locale) { - StringBuffer buffer = new StringBuffer(); - // Add version - final String version = Build.VERSION.RELEASE; - if (version.length() > 0) { - if (Character.isDigit(version.charAt(0))) { - // Release is a version, eg "3.1" - buffer.append(version); - } else { - // Release is a codename, eg "Honeycomb" - // In this case, use the previous release's version - buffer.append(PREVIOUS_VERSION); - } - } else { - // default to "1.0" - buffer.append("1.0"); - } - buffer.append("; "); - final String language = locale.getLanguage(); - if (language != null) { - buffer.append(convertObsoleteLanguageCodeToNew(language)); - final String country = locale.getCountry(); - if (country != null) { - buffer.append("-"); - buffer.append(country.toLowerCase()); - } - } else { - // default to "en" - buffer.append("en"); - } - buffer.append(";"); - // add the model for the release build - if ("REL".equals(Build.VERSION.CODENAME)) { - final String model = Build.MODEL; - if (model.length() > 0) { - buffer.append(" "); - buffer.append(model); - } - } - final String id = Build.ID; - if (id.length() > 0) { - buffer.append(" Build/"); - buffer.append(id); - } - String mobile = context.getResources().getText( - com.android.internal.R.string.web_user_agent_target_content).toString(); - final String base = context.getResources().getText( - com.android.internal.R.string.web_user_agent).toString(); - return String.format(base, buffer, mobile); - } - - /** - * @see android.webkit.WebSettings#setNavDump(boolean) - */ - @Override - @Deprecated - public void setNavDump(boolean enabled) { - mNavDump = enabled; - } - - /** - * @see android.webkit.WebSettings#getNavDump() - */ - @Override - @Deprecated - public boolean getNavDump() { - return mNavDump; - } - - /** - * @see android.webkit.WebSettings#setSupportZoom(boolean) - */ - @Override - public void setSupportZoom(boolean support) { - mSupportZoom = support; - mWebView.updateMultiTouchSupport(mContext); - } - - /** - * @see android.webkit.WebSettings#supportZoom() - */ - @Override - public boolean supportZoom() { - return mSupportZoom; - } - - /** - * @see android.webkit.WebSettings#setMediaPlaybackRequiresUserGesture(boolean) - */ - @Override - public void setMediaPlaybackRequiresUserGesture(boolean support) { - if (mMediaPlaybackRequiresUserGesture != support) { - mMediaPlaybackRequiresUserGesture = support; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getMediaPlaybackRequiresUserGesture() - */ - @Override - public boolean getMediaPlaybackRequiresUserGesture() { - return mMediaPlaybackRequiresUserGesture; - } - - /** - * @see android.webkit.WebSettings#setBuiltInZoomControls(boolean) - */ - @Override - public void setBuiltInZoomControls(boolean enabled) { - mBuiltInZoomControls = enabled; - mWebView.updateMultiTouchSupport(mContext); - } - - /** - * @see android.webkit.WebSettings#getBuiltInZoomControls() - */ - @Override - public boolean getBuiltInZoomControls() { - return mBuiltInZoomControls; - } - - /** - * @see android.webkit.WebSettings#setDisplayZoomControls(boolean) - */ - @Override - public void setDisplayZoomControls(boolean enabled) { - mDisplayZoomControls = enabled; - mWebView.updateMultiTouchSupport(mContext); - } - - /** - * @see android.webkit.WebSettings#getDisplayZoomControls() - */ - @Override - public boolean getDisplayZoomControls() { - return mDisplayZoomControls; - } - - /** - * @see android.webkit.WebSettings#setAllowFileAccess(boolean) - */ - @Override - public void setAllowFileAccess(boolean allow) { - mAllowFileAccess = allow; - } - - /** - * @see android.webkit.WebSettings#getAllowFileAccess() - */ - @Override - public boolean getAllowFileAccess() { - return mAllowFileAccess; - } - - /** - * @see android.webkit.WebSettings#setAllowContentAccess(boolean) - */ - @Override - public void setAllowContentAccess(boolean allow) { - mAllowContentAccess = allow; - } - - /** - * @see android.webkit.WebSettings#getAllowContentAccess() - */ - @Override - public boolean getAllowContentAccess() { - return mAllowContentAccess; - } - - /** - * @see android.webkit.WebSettings#setLoadWithOverviewMode(boolean) - */ - @Override - public void setLoadWithOverviewMode(boolean overview) { - mLoadWithOverviewMode = overview; - } - - /** - * @see android.webkit.WebSettings#getLoadWithOverviewMode() - */ - @Override - public boolean getLoadWithOverviewMode() { - return mLoadWithOverviewMode; - } - - /** - * @see android.webkit.WebSettings#setEnableSmoothTransition(boolean) - */ - @Override - public void setEnableSmoothTransition(boolean enable) { - mEnableSmoothTransition = enable; - } - - /** - * @see android.webkit.WebSettings#enableSmoothTransition() - */ - @Override - public boolean enableSmoothTransition() { - return mEnableSmoothTransition; - } - - /** - * @see android.webkit.WebSettings#setUseWebViewBackgroundForOverscrollBackground(boolean) - */ - @Override - @Deprecated - public void setUseWebViewBackgroundForOverscrollBackground(boolean view) { - mUseWebViewBackgroundForOverscroll = view; - } - - /** - * @see android.webkit.WebSettings#getUseWebViewBackgroundForOverscrollBackground() - */ - @Override - @Deprecated - public boolean getUseWebViewBackgroundForOverscrollBackground() { - return mUseWebViewBackgroundForOverscroll; - } - - /** - * @see android.webkit.WebSettings#setSaveFormData(boolean) - */ - @Override - public void setSaveFormData(boolean save) { - mSaveFormData = save; - } - - /** - * @see android.webkit.WebSettings#getSaveFormData() - */ - @Override - public boolean getSaveFormData() { - return mSaveFormData && !mPrivateBrowsingEnabled; - } - - /** - * @see android.webkit.WebSettings#setSavePassword(boolean) - */ - @Override - public void setSavePassword(boolean save) { - mSavePassword = save; - } - - /** - * @see android.webkit.WebSettings#getSavePassword() - */ - @Override - public boolean getSavePassword() { - return mSavePassword; - } - - /** - * @see android.webkit.WebSettings#setTextZoom(int) - */ - @Override - public synchronized void setTextZoom(int textZoom) { - if (mTextSize != textZoom) { - mTextSize = textZoom; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getTextZoom() - */ - @Override - public synchronized int getTextZoom() { - return mTextSize; - } - - /** - * Set the double-tap zoom of the page in percent. Default is 100. - * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom. - */ - public void setDoubleTapZoom(int doubleTapZoom) { - if (mDoubleTapZoom != doubleTapZoom) { - mDoubleTapZoom = doubleTapZoom; - mWebView.updateDoubleTapZoom(doubleTapZoom); - } - } - - /** - * Get the double-tap zoom of the page in percent. - * @return A percent value describing the double-tap zoom. - */ - public int getDoubleTapZoom() { - return mDoubleTapZoom; - } - - /** - * @see android.webkit.WebSettings#setDefaultZoom(android.webkit.WebSettingsClassic.ZoomDensity) - */ - @Override - public void setDefaultZoom(ZoomDensity zoom) { - if (mDefaultZoom != zoom) { - mDefaultZoom = zoom; - mWebView.adjustDefaultZoomDensity(zoom.value); - } - } - - /** - * @see android.webkit.WebSettings#getDefaultZoom() - */ - @Override - public ZoomDensity getDefaultZoom() { - return mDefaultZoom; - } - - /** - * @see android.webkit.WebSettings#setLightTouchEnabled(boolean) - */ - @Override - public void setLightTouchEnabled(boolean enabled) { - mLightTouchEnabled = enabled; - } - - /** - * @see android.webkit.WebSettings#getLightTouchEnabled() - */ - @Override - public boolean getLightTouchEnabled() { - return mLightTouchEnabled; - } - - /** - * @see android.webkit.WebSettings#setUseDoubleTree(boolean) - */ - @Override - @Deprecated - public synchronized void setUseDoubleTree(boolean use) { - return; - } - - /** - * @see android.webkit.WebSettings#getUseDoubleTree() - */ - @Override - @Deprecated - public synchronized boolean getUseDoubleTree() { - return false; - } - - /** - * @see android.webkit.WebSettings#setUserAgent(int) - */ - @Override - @Deprecated - public synchronized void setUserAgent(int ua) { - String uaString = null; - if (ua == 1) { - if (DESKTOP_USERAGENT.equals(mUserAgent)) { - return; // do nothing - } else { - uaString = DESKTOP_USERAGENT; - } - } else if (ua == 2) { - if (IPHONE_USERAGENT.equals(mUserAgent)) { - return; // do nothing - } else { - uaString = IPHONE_USERAGENT; - } - } else if (ua != 0) { - return; // do nothing - } - setUserAgentString(uaString); - } - - /** - * @see android.webkit.WebSettings#getUserAgent() - */ - @Override - @Deprecated - public synchronized int getUserAgent() { - if (DESKTOP_USERAGENT.equals(mUserAgent)) { - return 1; - } else if (IPHONE_USERAGENT.equals(mUserAgent)) { - return 2; - } else if (mUseDefaultUserAgent) { - return 0; - } - return -1; - } - - /** - * @see android.webkit.WebSettings#setUseWideViewPort(boolean) - */ - @Override - public synchronized void setUseWideViewPort(boolean use) { - if (mUseWideViewport != use) { - mUseWideViewport = use; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getUseWideViewPort() - */ - @Override - public synchronized boolean getUseWideViewPort() { - return mUseWideViewport; - } - - /** - * @see android.webkit.WebSettings#setSupportMultipleWindows(boolean) - */ - @Override - public synchronized void setSupportMultipleWindows(boolean support) { - if (mSupportMultipleWindows != support) { - mSupportMultipleWindows = support; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#supportMultipleWindows() - */ - @Override - public synchronized boolean supportMultipleWindows() { - return mSupportMultipleWindows; - } - - /** - * @see android.webkit.WebSettings#setLayoutAlgorithm(android.webkit.WebSettingsClassic.LayoutAlgorithm) - */ - @Override - public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) { - if (l == LayoutAlgorithm.TEXT_AUTOSIZING) { - throw new IllegalArgumentException( - "WebViewClassic does not support TEXT_AUTOSIZING layout mode"); - } - // XXX: This will only be affective if libwebcore was built with - // ANDROID_LAYOUT defined. - if (mLayoutAlgorithm != l) { - mLayoutAlgorithm = l; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getLayoutAlgorithm() - */ - @Override - public synchronized LayoutAlgorithm getLayoutAlgorithm() { - return mLayoutAlgorithm; - } - - /** - * @see android.webkit.WebSettings#setStandardFontFamily(java.lang.String) - */ - @Override - public synchronized void setStandardFontFamily(String font) { - if (font != null && !font.equals(mStandardFontFamily)) { - mStandardFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getStandardFontFamily() - */ - @Override - public synchronized String getStandardFontFamily() { - return mStandardFontFamily; - } - - /** - * @see android.webkit.WebSettings#setFixedFontFamily(java.lang.String) - */ - @Override - public synchronized void setFixedFontFamily(String font) { - if (font != null && !font.equals(mFixedFontFamily)) { - mFixedFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getFixedFontFamily() - */ - @Override - public synchronized String getFixedFontFamily() { - return mFixedFontFamily; - } - - /** - * @see android.webkit.WebSettings#setSansSerifFontFamily(java.lang.String) - */ - @Override - public synchronized void setSansSerifFontFamily(String font) { - if (font != null && !font.equals(mSansSerifFontFamily)) { - mSansSerifFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getSansSerifFontFamily() - */ - @Override - public synchronized String getSansSerifFontFamily() { - return mSansSerifFontFamily; - } - - /** - * @see android.webkit.WebSettings#setSerifFontFamily(java.lang.String) - */ - @Override - public synchronized void setSerifFontFamily(String font) { - if (font != null && !font.equals(mSerifFontFamily)) { - mSerifFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getSerifFontFamily() - */ - @Override - public synchronized String getSerifFontFamily() { - return mSerifFontFamily; - } - - /** - * @see android.webkit.WebSettings#setCursiveFontFamily(java.lang.String) - */ - @Override - public synchronized void setCursiveFontFamily(String font) { - if (font != null && !font.equals(mCursiveFontFamily)) { - mCursiveFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getCursiveFontFamily() - */ - @Override - public synchronized String getCursiveFontFamily() { - return mCursiveFontFamily; - } - - /** - * @see android.webkit.WebSettings#setFantasyFontFamily(java.lang.String) - */ - @Override - public synchronized void setFantasyFontFamily(String font) { - if (font != null && !font.equals(mFantasyFontFamily)) { - mFantasyFontFamily = font; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getFantasyFontFamily() - */ - @Override - public synchronized String getFantasyFontFamily() { - return mFantasyFontFamily; - } - - /** - * @see android.webkit.WebSettings#setMinimumFontSize(int) - */ - @Override - public synchronized void setMinimumFontSize(int size) { - size = pin(size); - if (mMinimumFontSize != size) { - mMinimumFontSize = size; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getMinimumFontSize() - */ - @Override - public synchronized int getMinimumFontSize() { - return mMinimumFontSize; - } - - /** - * @see android.webkit.WebSettings#setMinimumLogicalFontSize(int) - */ - @Override - public synchronized void setMinimumLogicalFontSize(int size) { - size = pin(size); - if (mMinimumLogicalFontSize != size) { - mMinimumLogicalFontSize = size; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getMinimumLogicalFontSize() - */ - @Override - public synchronized int getMinimumLogicalFontSize() { - return mMinimumLogicalFontSize; - } - - /** - * @see android.webkit.WebSettings#setDefaultFontSize(int) - */ - @Override - public synchronized void setDefaultFontSize(int size) { - size = pin(size); - if (mDefaultFontSize != size) { - mDefaultFontSize = size; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getDefaultFontSize() - */ - @Override - public synchronized int getDefaultFontSize() { - return mDefaultFontSize; - } - - /** - * @see android.webkit.WebSettings#setDefaultFixedFontSize(int) - */ - @Override - public synchronized void setDefaultFixedFontSize(int size) { - size = pin(size); - if (mDefaultFixedFontSize != size) { - mDefaultFixedFontSize = size; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getDefaultFixedFontSize() - */ - @Override - public synchronized int getDefaultFixedFontSize() { - return mDefaultFixedFontSize; - } - - /** - * Set the number of pages cached by the WebKit for the history navigation. - * @param size A non-negative integer between 0 (no cache) and 20 (max). - */ - public synchronized void setPageCacheCapacity(int size) { - if (size < 0) size = 0; - if (size > 20) size = 20; - if (mPageCacheCapacity != size) { - mPageCacheCapacity = size; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setLoadsImagesAutomatically(boolean) - */ - @Override - public synchronized void setLoadsImagesAutomatically(boolean flag) { - if (mLoadsImagesAutomatically != flag) { - mLoadsImagesAutomatically = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getLoadsImagesAutomatically() - */ - @Override - public synchronized boolean getLoadsImagesAutomatically() { - return mLoadsImagesAutomatically; - } - - /** - * @see android.webkit.WebSettings#setBlockNetworkImage(boolean) - */ - @Override - public synchronized void setBlockNetworkImage(boolean flag) { - if (mBlockNetworkImage != flag) { - mBlockNetworkImage = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getBlockNetworkImage() - */ - @Override - public synchronized boolean getBlockNetworkImage() { - return mBlockNetworkImage; - } - - /** - * @see android.webkit.WebSettings#setBlockNetworkLoads(boolean) - */ - @Override - public synchronized void setBlockNetworkLoads(boolean flag) { - if (mBlockNetworkLoads != flag) { - mBlockNetworkLoads = flag; - verifyNetworkAccess(); - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getBlockNetworkLoads() - */ - @Override - public synchronized boolean getBlockNetworkLoads() { - return mBlockNetworkLoads; - } - - - private void verifyNetworkAccess() { - if (!mBlockNetworkLoads) { - if (mContext.checkPermission("android.permission.INTERNET", - android.os.Process.myPid(), android.os.Process.myUid()) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException - ("Permission denied - " + - "application missing INTERNET permission"); - } - } - } - - /** - * @see android.webkit.WebSettings#setJavaScriptEnabled(boolean) - */ - @Override - public synchronized void setJavaScriptEnabled(boolean flag) { - if (mJavaScriptEnabled != flag) { - mJavaScriptEnabled = flag; - postSync(); - mWebView.updateJavaScriptEnabled(flag); - } - } - - /** - * @see android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs - */ - @Override - public synchronized void setAllowUniversalAccessFromFileURLs(boolean flag) { - if (mAllowUniversalAccessFromFileURLs != flag) { - mAllowUniversalAccessFromFileURLs = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setAllowFileAccessFromFileURLs - */ - @Override - public synchronized void setAllowFileAccessFromFileURLs(boolean flag) { - if (mAllowFileAccessFromFileURLs != flag) { - mAllowFileAccessFromFileURLs = flag; - postSync(); - } - } - - /** - * Tell the WebView to use Skia's hardware accelerated rendering path - * @param flag True if the WebView should use Skia's hw-accel path - */ - public synchronized void setHardwareAccelSkiaEnabled(boolean flag) { - if (mHardwareAccelSkia != flag) { - mHardwareAccelSkia = flag; - postSync(); - } - } - - /** - * @return True if the WebView is using hardware accelerated skia - */ - public synchronized boolean getHardwareAccelSkiaEnabled() { - return mHardwareAccelSkia; - } - - /** - * Tell the WebView to show the visual indicator - * @param flag True if the WebView should show the visual indicator - */ - public synchronized void setShowVisualIndicator(boolean flag) { - if (mShowVisualIndicator != flag) { - mShowVisualIndicator = flag; - postSync(); - } - } - - /** - * @return True if the WebView is showing the visual indicator - */ - public synchronized boolean getShowVisualIndicator() { - return mShowVisualIndicator; - } - - /** - * @see android.webkit.WebSettings#setPluginsEnabled(boolean) - */ - @Override - @Deprecated - public synchronized void setPluginsEnabled(boolean flag) { - setPluginState(flag ? PluginState.ON : PluginState.OFF); - } - - /** - * @see android.webkit.WebSettings#setPluginState(android.webkit.WebSettingsClassic.PluginState) - */ - @Override - public synchronized void setPluginState(PluginState state) { - if (mPluginState != state) { - mPluginState = state; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setPluginsPath(java.lang.String) - */ - @Override - @Deprecated - public synchronized void setPluginsPath(String pluginsPath) { - } - - /** - * @see android.webkit.WebSettings#setDatabasePath(java.lang.String) - */ - @Override - public synchronized void setDatabasePath(String databasePath) { - if (databasePath != null && !mDatabasePathHasBeenSet) { - mDatabasePath = databasePath; - mDatabasePathHasBeenSet = true; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setGeolocationDatabasePath(java.lang.String) - */ - @Override - public synchronized void setGeolocationDatabasePath(String databasePath) { - if (databasePath != null - && !databasePath.equals(mGeolocationDatabasePath)) { - mGeolocationDatabasePath = databasePath; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setAppCacheEnabled(boolean) - */ - @Override - public synchronized void setAppCacheEnabled(boolean flag) { - if (mAppCacheEnabled != flag) { - mAppCacheEnabled = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setAppCachePath(java.lang.String) - */ - @Override - public synchronized void setAppCachePath(String path) { - // We test for a valid path and for repeated setting on the native - // side, but we can avoid syncing in some simple cases. - if (mAppCachePath == null && path != null && !path.isEmpty()) { - mAppCachePath = path; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setAppCacheMaxSize(long) - */ - @Override - public synchronized void setAppCacheMaxSize(long appCacheMaxSize) { - if (appCacheMaxSize != mAppCacheMaxSize) { - mAppCacheMaxSize = appCacheMaxSize; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setDatabaseEnabled(boolean) - */ - @Override - public synchronized void setDatabaseEnabled(boolean flag) { - if (mDatabaseEnabled != flag) { - mDatabaseEnabled = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setDomStorageEnabled(boolean) - */ - @Override - public synchronized void setDomStorageEnabled(boolean flag) { - if (mDomStorageEnabled != flag) { - mDomStorageEnabled = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getDomStorageEnabled() - */ - @Override - public synchronized boolean getDomStorageEnabled() { - return mDomStorageEnabled; - } - - /** - * @see android.webkit.WebSettings#getDatabasePath() - */ - @Override - public synchronized String getDatabasePath() { - return mDatabasePath; - } - - /** - * @see android.webkit.WebSettings#getDatabaseEnabled() - */ - @Override - public synchronized boolean getDatabaseEnabled() { - return mDatabaseEnabled; - } - - /** - * Tell the WebView to enable WebWorkers API. - * @param flag True if the WebView should enable WebWorkers. - * Note that this flag only affects V8. JSC does not have - * an equivalent setting. - */ - public synchronized void setWorkersEnabled(boolean flag) { - if (mWorkersEnabled != flag) { - mWorkersEnabled = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#setGeolocationEnabled(boolean) - */ - @Override - public synchronized void setGeolocationEnabled(boolean flag) { - if (mGeolocationEnabled != flag) { - mGeolocationEnabled = flag; - postSync(); - } - } - - /** - * Sets whether XSS Auditor is enabled. - * Only used by LayoutTestController. - * @param flag Whether XSS Auditor should be enabled. - */ - public synchronized void setXSSAuditorEnabled(boolean flag) { - if (mXSSAuditorEnabled != flag) { - mXSSAuditorEnabled = flag; - postSync(); - } - } - - /** - * Enables/disables HTML5 link "prefetch" parameter. - */ - public synchronized void setLinkPrefetchEnabled(boolean flag) { - if (mLinkPrefetchEnabled != flag) { - mLinkPrefetchEnabled = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getJavaScriptEnabled() - */ - @Override - public synchronized boolean getJavaScriptEnabled() { - return mJavaScriptEnabled; - } - - /** - * @see android.webkit.WebSettings#getAllowUniversalFileAccessFromFileURLs - */ - @Override - public synchronized boolean getAllowUniversalAccessFromFileURLs() { - return mAllowUniversalAccessFromFileURLs; - } - - /** - * @see android.webkit.WebSettings#getAllowFileAccessFromFileURLs - */ - @Override - public synchronized boolean getAllowFileAccessFromFileURLs() { - return mAllowFileAccessFromFileURLs; - } - - /** - * @see android.webkit.WebSettings#getPluginsEnabled() - */ - @Override - @Deprecated - public synchronized boolean getPluginsEnabled() { - return mPluginState == PluginState.ON; - } - - /** - * @see android.webkit.WebSettings#getPluginState() - */ - @Override - public synchronized PluginState getPluginState() { - return mPluginState; - } - - /** - * @see android.webkit.WebSettings#getPluginsPath() - */ - @Override - @Deprecated - public synchronized String getPluginsPath() { - return ""; - } - - /** - * @see android.webkit.WebSettings#setJavaScriptCanOpenWindowsAutomatically(boolean) - */ - @Override - public synchronized void setJavaScriptCanOpenWindowsAutomatically( - boolean flag) { - if (mJavaScriptCanOpenWindowsAutomatically != flag) { - mJavaScriptCanOpenWindowsAutomatically = flag; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getJavaScriptCanOpenWindowsAutomatically() - */ - @Override - public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() { - return mJavaScriptCanOpenWindowsAutomatically; - } - - /** - * @see android.webkit.WebSettings#setDefaultTextEncodingName(java.lang.String) - */ - @Override - public synchronized void setDefaultTextEncodingName(String encoding) { - if (encoding != null && !encoding.equals(mDefaultTextEncoding)) { - mDefaultTextEncoding = encoding; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getDefaultTextEncodingName() - */ - @Override - public synchronized String getDefaultTextEncodingName() { - return mDefaultTextEncoding; - } - - /** - * @see android.webkit.WebSettings#setUserAgentString(java.lang.String) - */ - @Override - public synchronized void setUserAgentString(String ua) { - if (ua == null || ua.length() == 0) { - synchronized(sLockForLocaleSettings) { - Locale currentLocale = Locale.getDefault(); - if (!sLocale.equals(currentLocale)) { - sLocale = currentLocale; - mAcceptLanguage = getCurrentAcceptLanguage(); - } - } - ua = getCurrentUserAgent(); - mUseDefaultUserAgent = true; - } else { - mUseDefaultUserAgent = false; - } - - if (!ua.equals(mUserAgent)) { - mUserAgent = ua; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getUserAgentString() - */ - @Override - public synchronized String getUserAgentString() { - if (DESKTOP_USERAGENT.equals(mUserAgent) || - IPHONE_USERAGENT.equals(mUserAgent) || - !mUseDefaultUserAgent) { - return mUserAgent; - } - - boolean doPostSync = false; - synchronized(sLockForLocaleSettings) { - Locale currentLocale = Locale.getDefault(); - if (!sLocale.equals(currentLocale)) { - sLocale = currentLocale; - mUserAgent = getCurrentUserAgent(); - mAcceptLanguage = getCurrentAcceptLanguage(); - doPostSync = true; - } - } - if (doPostSync) { - postSync(); - } - return mUserAgent; - } - - /* package api to grab the Accept Language string. */ - /*package*/ synchronized String getAcceptLanguage() { - synchronized(sLockForLocaleSettings) { - Locale currentLocale = Locale.getDefault(); - if (!sLocale.equals(currentLocale)) { - sLocale = currentLocale; - mAcceptLanguage = getCurrentAcceptLanguage(); - } - } - return mAcceptLanguage; - } - - /* package */ boolean isNarrowColumnLayout() { - return getLayoutAlgorithm() == LayoutAlgorithm.NARROW_COLUMNS; - } - - /** - * @see android.webkit.WebSettings#setNeedInitialFocus(boolean) - */ - @Override - public void setNeedInitialFocus(boolean flag) { - if (mNeedInitialFocus != flag) { - mNeedInitialFocus = flag; - } - } - - /* Package api to get the choice whether it needs to set initial focus. */ - /* package */ boolean getNeedInitialFocus() { - return mNeedInitialFocus; - } - - /** - * @see android.webkit.WebSettings#setRenderPriority(android.webkit.WebSettingsClassic.RenderPriority) - */ - @Override - public synchronized void setRenderPriority(RenderPriority priority) { - if (mRenderPriority != priority) { - mRenderPriority = priority; - mEventHandler.sendMessage(Message.obtain(null, - EventHandler.PRIORITY)); - } - } - - /** - * @see android.webkit.WebSettings#setCacheMode(int) - */ - @Override - public void setCacheMode(int mode) { - if (mode != mOverrideCacheMode) { - mOverrideCacheMode = mode; - postSync(); - } - } - - /** - * @see android.webkit.WebSettings#getCacheMode() - */ - @Override - public int getCacheMode() { - return mOverrideCacheMode; - } - - /** - * If set, webkit alternately shrinks and expands images viewed outside - * of an HTML page to fit the screen. This conflicts with attempts by - * the UI to zoom in and out of an image, so it is set false by default. - * @param shrink Set true to let webkit shrink the standalone image to fit. - */ - public void setShrinksStandaloneImagesToFit(boolean shrink) { - if (mShrinksStandaloneImagesToFit != shrink) { - mShrinksStandaloneImagesToFit = shrink; - postSync(); - } - } - - /** - * Specify the maximum decoded image size. The default is - * 2 megs for small memory devices and 8 megs for large memory devices. - * @param size The maximum decoded size, or zero to set to the default. - */ - public void setMaximumDecodedImageSize(long size) { - if (mMaximumDecodedImageSize != size) { - mMaximumDecodedImageSize = size; - postSync(); - } - } - - /** - * Returns whether to use fixed viewport. Use fixed viewport - * whenever wide viewport is on. - */ - /* package */ boolean getUseFixedViewport() { - return getUseWideViewPort(); - } - - /** - * Returns whether private browsing is enabled. - */ - /* package */ boolean isPrivateBrowsingEnabled() { - return mPrivateBrowsingEnabled; - } - - /** - * Sets whether private browsing is enabled. - * @param flag Whether private browsing should be enabled. - */ - /* package */ synchronized void setPrivateBrowsingEnabled(boolean flag) { - if (mPrivateBrowsingEnabled != flag) { - mPrivateBrowsingEnabled = flag; - - // AutoFill is dependant on private browsing being enabled so - // reset it to take account of the new value of mPrivateBrowsingEnabled. - setAutoFillEnabled(mAutoFillEnabled); - - postSync(); - } - } - - /** - * Returns whether the viewport metatag can disable zooming - */ - public boolean forceUserScalable() { - return mForceUserScalable; - } - - /** - * Sets whether viewport metatag can disable zooming. - * @param flag Whether or not to forceably enable user scalable. - */ - public synchronized void setForceUserScalable(boolean flag) { - mForceUserScalable = flag; - } - - synchronized void setSyntheticLinksEnabled(boolean flag) { - if (mSyntheticLinksEnabled != flag) { - mSyntheticLinksEnabled = flag; - postSync(); - } - } - - public synchronized void setAutoFillEnabled(boolean enabled) { - // AutoFill is always disabled in private browsing mode. - boolean autoFillEnabled = enabled && !mPrivateBrowsingEnabled; - if (mAutoFillEnabled != autoFillEnabled) { - mAutoFillEnabled = autoFillEnabled; - postSync(); - } - } - - public synchronized boolean getAutoFillEnabled() { - return mAutoFillEnabled; - } - - public synchronized void setAutoFillProfile(AutoFillProfile profile) { - if (mAutoFillProfile != profile) { - mAutoFillProfile = profile; - postSync(); - } - } - - public synchronized AutoFillProfile getAutoFillProfile() { - return mAutoFillProfile; - } - - int getDoubleTapToastCount() { - return mDoubleTapToastCount; - } - - void setDoubleTapToastCount(int count) { - if (mDoubleTapToastCount != count) { - mDoubleTapToastCount = count; - // write the settings in the non-UI thread - mEventHandler.sendMessage(Message.obtain(null, - EventHandler.SET_DOUBLE_TAP_TOAST_COUNT)); - } - } - - public void setProperty(String key, String value) { - if (mWebView.nativeSetProperty(key, value)) { - mWebView.invalidate(); - } - } - - public String getProperty(String key) { - return mWebView.nativeGetProperty(key); - } - - /** - * Transfer messages from the queue to the new WebCoreThread. Called from - * WebCore thread. - */ - /*package*/ - synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) { - mBrowserFrame = frame; - if (DebugFlags.WEB_SETTINGS) { - junit.framework.Assert.assertTrue(frame.mNativeFrame != 0); - } - - SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, - Context.MODE_PRIVATE); - if (mDoubleTapToastCount > 0) { - mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT, - mDoubleTapToastCount); - } - nativeSync(frame.mNativeFrame); - mSyncPending = false; - mEventHandler.createHandler(); - } - - /** - * Let the Settings object know that our owner is being destroyed. - */ - /*package*/ - synchronized void onDestroyed() { - } - - private int pin(int size) { - // FIXME: 72 is just an arbitrary max text size value. - if (size < 1) { - return 1; - } else if (size > 72) { - return 72; - } - return size; - } - - /* Post a SYNC message to handle syncing the native settings. */ - private synchronized void postSync() { - // Only post if a sync is not pending - if (!mSyncPending) { - mSyncPending = mEventHandler.sendMessage( - Message.obtain(null, EventHandler.SYNC)); - } - } - - // Synchronize the native and java settings. - private native void nativeSync(int nativeFrame); -} diff --git a/core/java/android/webkit/WebStorageClassic.java b/core/java/android/webkit/WebStorageClassic.java deleted file mode 100644 index 62de5e6..0000000 --- a/core/java/android/webkit/WebStorageClassic.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.os.Handler; -import android.os.Message; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** @hide */ -public class WebStorageClassic extends WebStorage { - // Global instance of a WebStorage - private static WebStorageClassic sWebStorage; - - // Message ids - static final int UPDATE = 0; - static final int SET_QUOTA_ORIGIN = 1; - static final int DELETE_ORIGIN = 2; - static final int DELETE_ALL = 3; - static final int GET_ORIGINS = 4; - static final int GET_USAGE_ORIGIN = 5; - static final int GET_QUOTA_ORIGIN = 6; - - // Message ids on the UI thread - static final int RETURN_ORIGINS = 0; - static final int RETURN_USAGE_ORIGIN = 1; - static final int RETURN_QUOTA_ORIGIN = 2; - - private static final String ORIGINS = "origins"; - private static final String ORIGIN = "origin"; - private static final String CALLBACK = "callback"; - private static final String USAGE = "usage"; - private static final String QUOTA = "quota"; - - private Map <String, Origin> mOrigins; - - private Handler mHandler = null; - private Handler mUIHandler = null; - - /** - * @hide - * Message handler, UI side - * @hide - */ - public void createUIHandler() { - if (mUIHandler == null) { - mUIHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case RETURN_ORIGINS: { - Map values = (Map) msg.obj; - Map origins = (Map) values.get(ORIGINS); - ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK); - callback.onReceiveValue(origins); - } break; - - case RETURN_USAGE_ORIGIN: { - Map values = (Map) msg.obj; - ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); - callback.onReceiveValue((Long)values.get(USAGE)); - } break; - - case RETURN_QUOTA_ORIGIN: { - Map values = (Map) msg.obj; - ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); - callback.onReceiveValue((Long)values.get(QUOTA)); - } break; - } - } - }; - } - } - - /** - * Message handler, WebCore side - * @hide - */ - public synchronized void createHandler() { - if (mHandler == null) { - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case SET_QUOTA_ORIGIN: { - Origin website = (Origin) msg.obj; - nativeSetQuotaForOrigin(website.getOrigin(), - website.getQuota()); - } break; - - case DELETE_ORIGIN: { - Origin website = (Origin) msg.obj; - nativeDeleteOrigin(website.getOrigin()); - } break; - - case DELETE_ALL: - nativeDeleteAllData(); - break; - - case GET_ORIGINS: { - syncValues(); - ValueCallback callback = (ValueCallback) msg.obj; - Map origins = new HashMap(mOrigins); - Map values = new HashMap<String, Object>(); - values.put(CALLBACK, callback); - values.put(ORIGINS, origins); - postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); - } break; - - case GET_USAGE_ORIGIN: { - syncValues(); - Map values = (Map) msg.obj; - String origin = (String) values.get(ORIGIN); - ValueCallback callback = (ValueCallback) values.get(CALLBACK); - Origin website = mOrigins.get(origin); - Map retValues = new HashMap<String, Object>(); - retValues.put(CALLBACK, callback); - if (website != null) { - long usage = website.getUsage(); - retValues.put(USAGE, new Long(usage)); - } - postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues)); - } break; - - case GET_QUOTA_ORIGIN: { - syncValues(); - Map values = (Map) msg.obj; - String origin = (String) values.get(ORIGIN); - ValueCallback callback = (ValueCallback) values.get(CALLBACK); - Origin website = mOrigins.get(origin); - Map retValues = new HashMap<String, Object>(); - retValues.put(CALLBACK, callback); - if (website != null) { - long quota = website.getQuota(); - retValues.put(QUOTA, new Long(quota)); - } - postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues)); - } break; - - case UPDATE: - syncValues(); - break; - } - } - }; - } - } - - /* - * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(), - * we need to get the values from WebCore, but we cannot block while doing so - * as we used to do, as this could result in a full deadlock (other WebCore - * messages received while we are still blocked here, see http://b/2127737). - * - * We have to do everything asynchronously, by providing a callback function. - * We post a message on the WebCore thread (mHandler) that will get the result - * from WebCore, and we post it back on the UI thread (using mUIHandler). - * We can then use the callback function to return the value. - */ - - @Override - public void getOrigins(ValueCallback<Map> callback) { - if (callback != null) { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - syncValues(); - callback.onReceiveValue(mOrigins); - } else { - postMessage(Message.obtain(null, GET_ORIGINS, callback)); - } - } - } - - /** - * Returns a list of origins having a database - * should only be called from WebViewCore. - */ - Collection<Origin> getOriginsSync() { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - update(); - return mOrigins.values(); - } - return null; - } - - @Override - public void getUsageForOrigin(String origin, ValueCallback<Long> callback) { - if (callback == null) { - return; - } - if (origin == null) { - callback.onReceiveValue(null); - return; - } - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - syncValues(); - Origin website = mOrigins.get(origin); - callback.onReceiveValue(new Long(website.getUsage())); - } else { - HashMap values = new HashMap<String, Object>(); - values.put(ORIGIN, origin); - values.put(CALLBACK, callback); - postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values)); - } - } - - @Override - public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) { - if (callback == null) { - return; - } - if (origin == null) { - callback.onReceiveValue(null); - return; - } - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - syncValues(); - Origin website = mOrigins.get(origin); - callback.onReceiveValue(new Long(website.getUsage())); - } else { - HashMap values = new HashMap<String, Object>(); - values.put(ORIGIN, origin); - values.put(CALLBACK, callback); - postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values)); - } - } - - @Override - public void setQuotaForOrigin(String origin, long quota) { - if (origin != null) { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - nativeSetQuotaForOrigin(origin, quota); - } else { - postMessage(Message.obtain(null, SET_QUOTA_ORIGIN, - new Origin(origin, quota))); - } - } - } - - @Override - public void deleteOrigin(String origin) { - if (origin != null) { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - nativeDeleteOrigin(origin); - } else { - postMessage(Message.obtain(null, DELETE_ORIGIN, - new Origin(origin))); - } - } - } - - @Override - public void deleteAllData() { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - nativeDeleteAllData(); - } else { - postMessage(Message.obtain(null, DELETE_ALL)); - } - } - - /** - * Sets the maximum size of the ApplicationCache. - * This should only ever be called on the WebKit thread. - * Not part of the base-class API: this is only used by dump render tree. - */ - public void setAppCacheMaximumSize(long size) { - nativeSetAppCacheMaximumSize(size); - } - - /** - * Utility function to send a message to our handler - */ - private synchronized void postMessage(Message msg) { - if (mHandler != null) { - mHandler.sendMessage(msg); - } - } - - /** - * Utility function to send a message to the handler on the UI thread - */ - private void postUIMessage(Message msg) { - if (mUIHandler != null) { - mUIHandler.sendMessage(msg); - } - } - - /** - * Get the singleton instance of this class. - * @return The singleton {@link WebStorage} instance. - */ - public static WebStorageClassic getInstance() { - if (sWebStorage == null) { - sWebStorage = new WebStorageClassic(); - } - return sWebStorage; - } - - /** - * @hide - * Post a Sync request - */ - public void update() { - if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { - syncValues(); - } else { - postMessage(Message.obtain(null, UPDATE)); - } - } - - /** - * Run on the WebCore thread - * set the local values with the current ones - */ - private void syncValues() { - Set<String> tmp = nativeGetOrigins(); - mOrigins = new HashMap<String, Origin>(); - for (String origin : tmp) { - Origin website = new Origin(origin, - nativeGetQuotaForOrigin(origin), - nativeGetUsageForOrigin(origin)); - mOrigins.put(origin, website); - } - } - - WebStorageClassic() {} - - // Native functions - private static native Set nativeGetOrigins(); - private static native long nativeGetUsageForOrigin(String origin); - private static native long nativeGetQuotaForOrigin(String origin); - private static native void nativeSetQuotaForOrigin(String origin, long quota); - private static native void nativeDeleteOrigin(String origin); - private static native void nativeDeleteAllData(); - private static native void nativeSetAppCacheMaximumSize(long size); -} diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java deleted file mode 100644 index 911073d..0000000 --- a/core/java/android/webkit/WebTextView.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2007 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 android.webkit; - -import android.util.Log; - -import java.net.MalformedURLException; -import java.net.URL; - -// TODO: Move these to a better place. -/* package */ abstract class WebTextView { - - private static final String LOGTAG = "WebTextView"; - - // Types used with setType. Keep in sync with CachedInput.h - static final int NORMAL_TEXT_FIELD = 0; - static final int TEXT_AREA = 1; - static final int PASSWORD = 2; - static final int SEARCH = 3; - static final int EMAIL = 4; - static final int NUMBER = 5; - static final int TELEPHONE = 6; - static final int URL = 7; - - static final int FORM_NOT_AUTOFILLABLE = -1; - - static String urlForAutoCompleteData(String urlString) { - // Remove any fragment or query string. - URL url = null; - try { - url = new URL(urlString); - } catch (MalformedURLException e) { - Log.e(LOGTAG, "Unable to parse URL "+url); - } - - return url != null ? url.getProtocol() + "://" + url.getHost() + url.getPath() : null; - } - -} diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java deleted file mode 100644 index 3c377d3..0000000 --- a/core/java/android/webkit/WebViewClassic.java +++ /dev/null @@ -1,8810 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.accessibilityservice.AccessibilityServiceInfo; -import android.animation.ObjectAnimator; -import android.annotation.Widget; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.ComponentCallbacks2; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.database.DataSetObserver; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapShader; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.DrawFilter; -import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; -import android.graphics.Picture; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Region; -import android.graphics.RegionIterator; -import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import android.net.Proxy; -import android.net.ProxyProperties; -import android.net.Uri; -import android.net.http.SslCertificate; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.CancellationSignal; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.print.PrintDocumentAdapter; -import android.security.KeyChain; -import android.text.Editable; -import android.text.InputType; -import android.text.Selection; -import android.text.TextUtils; -import android.util.AndroidRuntimeException; -import android.util.DisplayMetrics; -import android.util.EventLog; -import android.util.Log; -import android.view.Gravity; -import android.view.HapticFeedbackConstants; -import android.view.HardwareCanvas; -import android.view.InputDevice; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.SoundEffectConstants; -import android.view.VelocityTracker; -import android.view.View; -import android.view.View.MeasureSpec; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.ViewRootImpl; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeProvider; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.webkit.WebView.HitTestResult; -import android.webkit.WebView.PictureListener; -import android.webkit.WebViewCore.DrawData; -import android.webkit.WebViewCore.EventHub; -import android.webkit.WebViewCore.TextFieldInitData; -import android.webkit.WebViewCore.TextSelectionData; -import android.webkit.WebViewCore.WebKitHitTest; -import android.widget.AbsoluteLayout; -import android.widget.Adapter; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.CheckedTextView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.OverScroller; -import android.widget.PopupWindow; -import android.widget.Scroller; -import android.widget.TextView; -import android.widget.Toast; - -import junit.framework.Assert; - -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.Vector; - -/** - * Implements a backend provider for the {@link WebView} public API. - * @hide - */ -// TODO: Check if any WebView published API methods are called from within here, and if so -// we should bounce the call out via the proxy to enable any sub-class to override it. -@Widget -@SuppressWarnings("deprecation") -public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate, - WebViewProvider.ViewDelegate { - /** - * InputConnection used for ContentEditable. This captures changes - * to the text and sends them either as key strokes or text changes. - */ - class WebViewInputConnection extends BaseInputConnection { - // Used for mapping characters to keys typed. - private KeyCharacterMap mKeyCharacterMap; - private boolean mIsKeySentByMe; - private int mInputType; - private int mImeOptions; - private String mHint; - private int mMaxLength; - private boolean mIsAutoFillable; - private boolean mIsAutoCompleteEnabled; - private String mName; - private int mBatchLevel; - - public WebViewInputConnection() { - super(mWebView, true); - } - - public void setAutoFillable(int queryId) { - mIsAutoFillable = getSettings().getAutoFillEnabled() - && (queryId != WebTextView.FORM_NOT_AUTOFILLABLE); - int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; - if (variation != EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD - && (mIsAutoFillable || mIsAutoCompleteEnabled)) { - if (mName != null && mName.length() > 0) { - requestFormData(mName, mFieldPointer, mIsAutoFillable, - mIsAutoCompleteEnabled); - } - } - } - - @Override - public boolean beginBatchEdit() { - if (mBatchLevel == 0) { - beginTextBatch(); - } - mBatchLevel++; - return false; - } - - @Override - public boolean endBatchEdit() { - mBatchLevel--; - if (mBatchLevel == 0) { - commitTextBatch(); - } - return false; - } - - public boolean getIsAutoFillable() { - return mIsAutoFillable; - } - - @Override - public boolean sendKeyEvent(KeyEvent event) { - // Some IMEs send key events directly using sendKeyEvents. - // WebViewInputConnection should treat these as text changes. - if (!mIsKeySentByMe) { - if (event.getAction() == KeyEvent.ACTION_UP) { - if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { - return deleteSurroundingText(1, 0); - } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) { - return deleteSurroundingText(0, 1); - } else if (event.getUnicodeChar() != 0){ - String newComposingText = - Character.toString((char)event.getUnicodeChar()); - return commitText(newComposingText, 1); - } - } else if (event.getAction() == KeyEvent.ACTION_DOWN && - (event.getKeyCode() == KeyEvent.KEYCODE_DEL - || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL - || event.getUnicodeChar() != 0)) { - return true; // only act on action_down - } - } - return super.sendKeyEvent(event); - } - - public void setTextAndKeepSelection(CharSequence text) { - Editable editable = getEditable(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - text = limitReplaceTextByMaxLength(text, editable.length()); - editable.replace(0, editable.length(), text); - restartInput(); - // Keep the previous selection. - selectionStart = Math.min(selectionStart, editable.length()); - selectionEnd = Math.min(selectionEnd, editable.length()); - setSelection(selectionStart, selectionEnd); - finishComposingText(); - } - - public void replaceSelection(CharSequence text) { - Editable editable = getEditable(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart); - setNewText(selectionStart, selectionEnd, text); - editable.replace(selectionStart, selectionEnd, text); - restartInput(); - // Move caret to the end of the new text - int newCaret = selectionStart + text.length(); - setSelection(newCaret, newCaret); - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - Editable editable = getEditable(); - int start = getComposingSpanStart(editable); - int end = getComposingSpanEnd(editable); - if (start < 0 || end < 0) { - start = Selection.getSelectionStart(editable); - end = Selection.getSelectionEnd(editable); - } - if (end < start) { - int temp = end; - end = start; - start = temp; - } - CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start); - setNewText(start, end, limitedText); - if (limitedText != text) { - newCursorPosition -= text.length() - limitedText.length(); - } - super.setComposingText(limitedText, newCursorPosition); - updateSelection(); - if (limitedText != text) { - int lastCaret = start + limitedText.length(); - finishComposingText(); - setSelection(lastCaret, lastCaret); - } - return true; - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - setComposingText(text, newCursorPosition); - finishComposingText(); - return true; - } - - @Override - public boolean deleteSurroundingText(int leftLength, int rightLength) { - // This code is from BaseInputConnection#deleteSurroundText. - // We have to delete the same text in webkit. - Editable content = getEditable(); - int a = Selection.getSelectionStart(content); - int b = Selection.getSelectionEnd(content); - - if (a > b) { - int tmp = a; - a = b; - b = tmp; - } - - int ca = getComposingSpanStart(content); - int cb = getComposingSpanEnd(content); - if (cb < ca) { - int tmp = ca; - ca = cb; - cb = tmp; - } - if (ca != -1 && cb != -1) { - if (ca < a) a = ca; - if (cb > b) b = cb; - } - - int endDelete = Math.min(content.length(), b + rightLength); - if (endDelete > b) { - setNewText(b, endDelete, ""); - } - int startDelete = Math.max(0, a - leftLength); - if (startDelete < a) { - setNewText(startDelete, a, ""); - } - return super.deleteSurroundingText(leftLength, rightLength); - } - - @Override - public boolean performEditorAction(int editorAction) { - - boolean handled = true; - switch (editorAction) { - case EditorInfo.IME_ACTION_NEXT: - mWebView.requestFocus(View.FOCUS_FORWARD); - break; - case EditorInfo.IME_ACTION_PREVIOUS: - mWebView.requestFocus(View.FOCUS_BACKWARD); - break; - case EditorInfo.IME_ACTION_DONE: - WebViewClassic.this.hideSoftKeyboard(); - break; - case EditorInfo.IME_ACTION_GO: - case EditorInfo.IME_ACTION_SEARCH: - WebViewClassic.this.hideSoftKeyboard(); - String text = getEditable().toString(); - passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_ENTER)); - passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP, - KeyEvent.KEYCODE_ENTER)); - break; - - default: - handled = super.performEditorAction(editorAction); - break; - } - - return handled; - } - - public void initEditorInfo(WebViewCore.TextFieldInitData initData) { - int type = initData.mType; - int inputType = InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; - int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI - | EditorInfo.IME_FLAG_NO_FULLSCREEN; - if (!initData.mIsSpellCheckEnabled) { - inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; - } - if (WebTextView.TEXT_AREA != type) { - if (initData.mIsTextFieldNext) { - imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; - } - if (initData.mIsTextFieldPrev) { - imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; - } - } - int action = EditorInfo.IME_ACTION_GO; - switch (type) { - case WebTextView.NORMAL_TEXT_FIELD: - break; - case WebTextView.TEXT_AREA: - inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE - | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES - | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; - action = EditorInfo.IME_ACTION_NONE; - break; - case WebTextView.PASSWORD: - inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD; - break; - case WebTextView.SEARCH: - action = EditorInfo.IME_ACTION_SEARCH; - break; - case WebTextView.EMAIL: - // inputType needs to be overwritten because of the different text variation. - inputType = InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; - break; - case WebTextView.NUMBER: - // inputType needs to be overwritten because of the different class. - inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL - | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL; - // Number and telephone do not have both a Tab key and an - // action, so set the action to NEXT - break; - case WebTextView.TELEPHONE: - // inputType needs to be overwritten because of the different class. - inputType = InputType.TYPE_CLASS_PHONE; - break; - case WebTextView.URL: - // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so - // exclude it for now. - inputType |= InputType.TYPE_TEXT_VARIATION_URI; - break; - default: - break; - } - imeOptions |= action; - mHint = initData.mLabel; - mInputType = inputType; - mImeOptions = imeOptions; - mMaxLength = initData.mMaxLength; - mIsAutoCompleteEnabled = initData.mIsAutoCompleteEnabled; - mName = initData.mName; - mAutoCompletePopup.clearAdapter(); - } - - public void setupEditorInfo(EditorInfo outAttrs) { - outAttrs.inputType = mInputType; - outAttrs.imeOptions = mImeOptions; - outAttrs.hintText = mHint; - outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT); - - Editable editable = getEditable(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - if (selectionStart < 0 || selectionEnd < 0) { - selectionStart = editable.length(); - selectionEnd = selectionStart; - } - outAttrs.initialSelStart = selectionStart; - outAttrs.initialSelEnd = selectionEnd; - } - - @Override - public boolean setSelection(int start, int end) { - boolean result = super.setSelection(start, end); - updateSelection(); - return result; - } - - @Override - public boolean setComposingRegion(int start, int end) { - boolean result = super.setComposingRegion(start, end); - updateSelection(); - return result; - } - - /** - * Send the selection and composing spans to the IME. - */ - private void updateSelection() { - Editable editable = getEditable(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - int composingStart = getComposingSpanStart(editable); - int composingEnd = getComposingSpanEnd(editable); - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.updateSelection(mWebView, selectionStart, selectionEnd, - composingStart, composingEnd); - } - } - - /** - * Sends a text change to webkit indirectly. If it is a single- - * character add or delete, it sends it as a key stroke. If it cannot - * be represented as a key stroke, it sends it as a field change. - * @param start The start offset (inclusive) of the text being changed. - * @param end The end offset (exclusive) of the text being changed. - * @param text The new text to replace the changed text. - */ - private void setNewText(int start, int end, CharSequence text) { - mIsKeySentByMe = true; - Editable editable = getEditable(); - CharSequence original = editable.subSequence(start, end); - boolean isCharacterAdd = false; - boolean isCharacterDelete = false; - int textLength = text.length(); - int originalLength = original.length(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - if (selectionStart == selectionEnd) { - if (textLength > originalLength) { - isCharacterAdd = (textLength == originalLength + 1) - && TextUtils.regionMatches(text, 0, original, 0, - originalLength); - } else if (originalLength > textLength) { - isCharacterDelete = (textLength == originalLength - 1) - && TextUtils.regionMatches(text, 0, original, 0, - textLength); - } - } - if (isCharacterAdd) { - sendCharacter(text.charAt(textLength - 1)); - } else if (isCharacterDelete) { - sendKey(KeyEvent.KEYCODE_DEL); - } else if ((textLength != originalLength) || - !TextUtils.regionMatches(text, 0, original, 0, - textLength)) { - // Send a message so that key strokes and text replacement - // do not come out of order. - Message replaceMessage = mPrivateHandler.obtainMessage( - REPLACE_TEXT, start, end, text.toString()); - mPrivateHandler.sendMessage(replaceMessage); - } - if (mAutoCompletePopup != null) { - StringBuilder newText = new StringBuilder(); - newText.append(editable.subSequence(0, start)); - newText.append(text); - newText.append(editable.subSequence(end, editable.length())); - mAutoCompletePopup.setText(newText.toString()); - } - mIsKeySentByMe = false; - } - - /** - * Send a single character to the WebView as a key down and up event. - * @param c The character to be sent. - */ - private void sendCharacter(char c) { - if (mKeyCharacterMap == null) { - mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); - } - char[] chars = new char[1]; - chars[0] = c; - KeyEvent[] events = mKeyCharacterMap.getEvents(chars); - if (events != null) { - for (KeyEvent event : events) { - sendKeyEvent(event); - } - } else { - Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0); - mPrivateHandler.sendMessage(msg); - } - } - - /** - * Send a key event for a specific key code, not a standard - * unicode character. - * @param keyCode The key code to send. - */ - private void sendKey(int keyCode) { - long eventTime = SystemClock.uptimeMillis(); - sendKeyEvent(new KeyEvent(eventTime, eventTime, - KeyEvent.ACTION_DOWN, keyCode, 0, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - KeyEvent.FLAG_SOFT_KEYBOARD)); - sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, - KeyEvent.ACTION_UP, keyCode, 0, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - KeyEvent.FLAG_SOFT_KEYBOARD)); - } - - private CharSequence limitReplaceTextByMaxLength(CharSequence text, - int numReplaced) { - if (mMaxLength > 0) { - Editable editable = getEditable(); - int maxReplace = mMaxLength - editable.length() + numReplaced; - if (maxReplace < text.length()) { - maxReplace = Math.max(maxReplace, 0); - // New length is greater than the maximum. trim it down. - text = text.subSequence(0, maxReplace); - } - } - return text; - } - - private void restartInput() { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - // Since the text has changed, do not allow the IME to replace the - // existing text as though it were a completion. - imm.restartInput(mWebView); - } - } - } - - private class PastePopupWindow extends PopupWindow implements View.OnClickListener { - private ViewGroup mContentView; - private TextView mPasteTextView; - - public PastePopupWindow() { - super(mContext, null, - com.android.internal.R.attr.textSelectHandleWindowStyle); - setClippingEnabled(true); - LinearLayout linearLayout = new LinearLayout(mContext); - linearLayout.setOrientation(LinearLayout.HORIZONTAL); - mContentView = linearLayout; - mContentView.setBackgroundResource( - com.android.internal.R.drawable.text_edit_paste_window); - - LayoutInflater inflater = (LayoutInflater)mContext. - getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - mPasteTextView = (TextView) inflater.inflate( - com.android.internal.R.layout.text_edit_action_popup_text, null); - mPasteTextView.setLayoutParams(wrapContent); - mContentView.addView(mPasteTextView); - mPasteTextView.setText(com.android.internal.R.string.paste); - mPasteTextView.setOnClickListener(this); - this.setContentView(mContentView); - } - - public void show(Point cursorBottom, Point cursorTop, - int windowLeft, int windowTop) { - measureContent(); - - int width = mContentView.getMeasuredWidth(); - int height = mContentView.getMeasuredHeight(); - int y = cursorTop.y - height; - int x = cursorTop.x - (width / 2); - if (y < windowTop) { - // There's not enough room vertically, move it below the - // handle. - ensureSelectionHandles(); - y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight(); - x = cursorBottom.x - (width / 2); - } - if (x < windowLeft) { - x = windowLeft; - } - if (!isShowing()) { - showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y); - } - update(x, y, width, height); - } - - public void hide() { - dismiss(); - } - - @Override - public void onClick(View view) { - pasteFromClipboard(); - selectionDone(); - } - - protected void measureContent() { - final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); - mContentView.measure( - View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels, - View.MeasureSpec.AT_MOST), - View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels, - View.MeasureSpec.AT_MOST)); - } - } - - // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing - // the screen all-the-time. Good for profiling our drawing code - static private final boolean AUTO_REDRAW_HACK = false; - - // The rate at which edit text is scrolled in content pixels per millisecond - static private final float TEXT_SCROLL_RATE = 0.01f; - - // The presumed scroll rate for the first scroll of edit text - static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16; - - // Buffer pixels of the caret rectangle when moving edit text into view - // after resize. - static private final int EDIT_RECT_BUFFER = 10; - - static private final long SELECTION_HANDLE_ANIMATION_MS = 150; - - // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK - private boolean mAutoRedraw; - - // Reference to the AlertDialog displayed by InvokeListBox. - // It's used to dismiss the dialog in destroy if not done before. - private AlertDialog mListBoxDialog = null; - - // Reference to the save password dialog so it can be dimissed in - // destroy if not done before. - private AlertDialog mSavePasswordDialog = null; - - static final String LOGTAG = "webview"; - - private ZoomManager mZoomManager; - - private final Rect mInvScreenRect = new Rect(); - private final Rect mScreenRect = new Rect(); - private final RectF mVisibleContentRect = new RectF(); - private boolean mIsWebViewVisible = true; - WebViewInputConnection mInputConnection = null; - private int mFieldPointer; - private PastePopupWindow mPasteWindow; - private AutoCompletePopup mAutoCompletePopup; - Rect mEditTextContentBounds = new Rect(); - Rect mEditTextContent = new Rect(); - int mEditTextLayerId; - boolean mIsEditingText = false; - ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>(); - boolean mIsBatchingTextChanges = false; - private long mLastEditScroll = 0; - - private static class OnTrimMemoryListener implements ComponentCallbacks2 { - private static OnTrimMemoryListener sInstance = null; - - static void init(Context c) { - if (sInstance == null) { - sInstance = new OnTrimMemoryListener(c.getApplicationContext()); - } - } - - private OnTrimMemoryListener(Context c) { - c.registerComponentCallbacks(this); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - // Ignore - } - - @Override - public void onLowMemory() { - // Ignore - } - - @Override - public void onTrimMemory(int level) { - if (DebugFlags.WEB_VIEW) { - Log.d("WebView", "onTrimMemory: " + level); - } - // When framework reset EGL context during high memory pressure, all - // the existing GL resources for the html5 video will be destroyed - // at native side. - // Here we just need to clean up the Surface Texture which is static. - if (level > TRIM_MEMORY_UI_HIDDEN) { - HTML5VideoInline.cleanupSurfaceTexture(); - HTML5VideoView.release(); - } - WebViewClassic.nativeOnTrimMemory(level); - } - } - - // A final CallbackProxy shared by WebViewCore and BrowserFrame. - private CallbackProxy mCallbackProxy; - - private WebViewDatabaseClassic mDatabase; - - // SSL certificate for the main top-level page (if secure) - private SslCertificate mCertificate; - - // Native WebView pointer that is 0 until the native object has been - // created. - private int mNativeClass; - // This would be final but it needs to be set to null when the WebView is - // destroyed. - private WebViewCore mWebViewCore; - // Handler for dispatching UI messages. - /* package */ final Handler mPrivateHandler = new PrivateHandler(); - // Used to ignore changes to webkit text that arrives to the UI side after - // more key events. - private int mTextGeneration; - - /* package */ void incrementTextGeneration() { mTextGeneration++; } - - // Used by WebViewCore to create child views. - /* package */ ViewManager mViewManager; - - // Used to display in full screen mode - PluginFullScreenHolder mFullScreenHolder; - - /** - * Position of the last touch event in pixels. - * Use integer to prevent loss of dragging delta calculation accuracy; - * which was done in float and converted to integer, and resulted in gradual - * and compounding touch position and view dragging mismatch. - */ - private int mLastTouchX; - private int mLastTouchY; - private int mStartTouchX; - private int mStartTouchY; - private float mAverageAngle; - - /** - * Time of the last touch event. - */ - private long mLastTouchTime; - - /** - * Time of the last time sending touch event to WebViewCore - */ - private long mLastSentTouchTime; - - /** - * The minimum elapsed time before sending another ACTION_MOVE event to - * WebViewCore. This really should be tuned for each type of the devices. - * For example in Google Map api test case, it takes Dream device at least - * 150ms to do a full cycle in the WebViewCore by processing a touch event, - * triggering the layout and drawing the picture. While the same process - * takes 60+ms on the current high speed device. If we make - * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent - * to WebViewCore queue and the real layout and draw events will be pushed - * to further, which slows down the refresh rate. Choose 50 to favor the - * current high speed devices. For Dream like devices, 100 is a better - * choice. Maybe make this in the buildspec later. - * (Update 12/14/2010: changed to 0 since current device should be able to - * handle the raw events and Map team voted to have the raw events too. - */ - private static final int TOUCH_SENT_INTERVAL = 0; - private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL; - - /** - * Helper class to get velocity for fling - */ - VelocityTracker mVelocityTracker; - private int mMaximumFling; - private float mLastVelocity; - private float mLastVelX; - private float mLastVelY; - - // The id of the native layer being scrolled. - private int mCurrentScrollingLayerId; - private Rect mScrollingLayerRect = new Rect(); - - // only trigger accelerated fling if the new velocity is at least - // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity - private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f; - - /** - * Touch mode - * TODO: Some of this is now unnecessary as it is handled by - * WebInputTouchDispatcher (such as click, long press, and double tap). - */ - private int mTouchMode = TOUCH_DONE_MODE; - private static final int TOUCH_INIT_MODE = 1; - private static final int TOUCH_DRAG_START_MODE = 2; - private static final int TOUCH_DRAG_MODE = 3; - private static final int TOUCH_SHORTPRESS_START_MODE = 4; - private static final int TOUCH_SHORTPRESS_MODE = 5; - private static final int TOUCH_DOUBLE_TAP_MODE = 6; - private static final int TOUCH_DONE_MODE = 7; - private static final int TOUCH_PINCH_DRAG = 8; - private static final int TOUCH_DRAG_LAYER_MODE = 9; - private static final int TOUCH_DRAG_TEXT_MODE = 10; - - // true when the touch movement exceeds the slop - private boolean mConfirmMove; - private boolean mTouchInEditText; - - // Whether or not to draw the cursor ring. - private boolean mDrawCursorRing = true; - - // true if onPause has been called (and not onResume) - private boolean mIsPaused; - - private HitTestResult mInitialHitTestResult; - private WebKitHitTest mFocusedNode; - - /** - * Customizable constant - */ - // pre-computed square of ViewConfiguration.getScaledTouchSlop() - private int mTouchSlopSquare; - // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop() - private int mDoubleTapSlopSquare; - // pre-computed density adjusted navigation slop - private int mNavSlop; - // This should be ViewConfiguration.getTapTimeout() - // But system time out is 100ms, which is too short for the browser. - // In the browser, if it switches out of tap too soon, jump tap won't work. - // In addition, a double tap on a trackpad will always have a duration of - // 300ms, so this value must be at least that (otherwise we will timeout the - // first tap and convert it to a long press). - private static final int TAP_TIMEOUT = 300; - // This should be ViewConfiguration.getLongPressTimeout() - // But system time out is 500ms, which is too short for the browser. - // With a short timeout, it's difficult to treat trigger a short press. - private static final int LONG_PRESS_TIMEOUT = 1000; - // needed to avoid flinging after a pause of no movement - private static final int MIN_FLING_TIME = 250; - // draw unfiltered after drag is held without movement - private static final int MOTIONLESS_TIME = 100; - // The amount of content to overlap between two screens when going through - // pages with the space bar, in pixels. - private static final int PAGE_SCROLL_OVERLAP = 24; - - /** - * These prevent calling requestLayout if either dimension is fixed. This - * depends on the layout parameters and the measure specs. - */ - boolean mWidthCanMeasure; - boolean mHeightCanMeasure; - - // Remember the last dimensions we sent to the native side so we can avoid - // sending the same dimensions more than once. - int mLastWidthSent; - int mLastHeightSent; - // Since view height sent to webkit could be fixed to avoid relayout, this - // value records the last sent actual view height. - int mLastActualHeightSent; - - private int mContentWidth; // cache of value from WebViewCore - private int mContentHeight; // cache of value from WebViewCore - - // Need to have the separate control for horizontal and vertical scrollbar - // style than the View's single scrollbar style - private boolean mOverlayHorizontalScrollbar = true; - private boolean mOverlayVerticalScrollbar = false; - - // our standard speed. this way small distances will be traversed in less - // time than large distances, but we cap the duration, so that very large - // distances won't take too long to get there. - private static final int STD_SPEED = 480; // pixels per second - // time for the longest scroll animation - private static final int MAX_DURATION = 750; // milliseconds - - // Used by OverScrollGlow - OverScroller mScroller; - Scroller mEditTextScroller; - - private boolean mInOverScrollMode = false; - private static Paint mOverScrollBackground; - private static Paint mOverScrollBorder; - - private boolean mWrapContent; - private static final int MOTIONLESS_FALSE = 0; - private static final int MOTIONLESS_PENDING = 1; - private static final int MOTIONLESS_TRUE = 2; - private static final int MOTIONLESS_IGNORE = 3; - private int mHeldMotionless; - - // Lazily-instantiated instance for injecting accessibility. - private AccessibilityInjector mAccessibilityInjector; - - /** - * How long the caret handle will last without being touched. - */ - private static final long CARET_HANDLE_STAMINA_MS = 3000; - - private Drawable mSelectHandleLeft; - private Drawable mSelectHandleRight; - private Drawable mSelectHandleCenter; - private Point mSelectOffset; - private Point mSelectCursorBase = new Point(); - private Rect mSelectHandleBaseBounds = new Rect(); - private int mSelectCursorBaseLayerId; - private QuadF mSelectCursorBaseTextQuad = new QuadF(); - private Point mSelectCursorExtent = new Point(); - private Rect mSelectHandleExtentBounds = new Rect(); - private int mSelectCursorExtentLayerId; - private QuadF mSelectCursorExtentTextQuad = new QuadF(); - private Point mSelectDraggingCursor; - private QuadF mSelectDraggingTextQuad; - private boolean mIsCaretSelection; - static final int HANDLE_ID_BASE = 0; - static final int HANDLE_ID_EXTENT = 1; - - // the color used to highlight the touch rectangles - static final int HIGHLIGHT_COLOR = 0x6633b5e5; - // the region indicating where the user touched on the screen - private Region mTouchHighlightRegion = new Region(); - // the paint for the touch highlight - private Paint mTouchHightlightPaint = new Paint(); - // debug only - private static final boolean DEBUG_TOUCH_HIGHLIGHT = true; - private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000; - private Paint mTouchCrossHairColor; - private int mTouchHighlightX; - private int mTouchHighlightY; - private boolean mShowTapHighlight; - - // Basically this proxy is used to tell the Video to update layer tree at - // SetBaseLayer time and to pause when WebView paused. - private HTML5VideoViewProxy mHTML5VideoViewProxy; - - // If we are using a set picture, don't send view updates to webkit - private boolean mBlockWebkitViewMessages = false; - - // cached value used to determine if we need to switch drawing models - private boolean mHardwareAccelSkia = false; - - /* - * Private message ids - */ - private static final int REMEMBER_PASSWORD = 1; - private static final int NEVER_REMEMBER_PASSWORD = 2; - private static final int SWITCH_TO_SHORTPRESS = 3; - private static final int SWITCH_TO_LONGPRESS = 4; - private static final int RELEASE_SINGLE_TAP = 5; - private static final int REQUEST_FORM_DATA = 6; - private static final int DRAG_HELD_MOTIONLESS = 8; - private static final int PREVENT_DEFAULT_TIMEOUT = 10; - private static final int SCROLL_SELECT_TEXT = 11; - - - private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD; - private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT; - - /* - * Package message ids - */ - static final int SCROLL_TO_MSG_ID = 101; - static final int NEW_PICTURE_MSG_ID = 105; - static final int WEBCORE_INITIALIZED_MSG_ID = 107; - static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108; - static final int UPDATE_ZOOM_RANGE = 109; - static final int TAKE_FOCUS = 110; - static final int CLEAR_TEXT_ENTRY = 111; - static final int UPDATE_TEXT_SELECTION_MSG_ID = 112; - static final int SHOW_RECT_MSG_ID = 113; - static final int LONG_PRESS_CENTER = 114; - static final int PREVENT_TOUCH_ID = 115; - static final int WEBCORE_NEED_TOUCH_EVENTS = 116; - // obj=Rect in doc coordinates - static final int INVAL_RECT_MSG_ID = 117; - static final int REQUEST_KEYBOARD = 118; - static final int SHOW_FULLSCREEN = 120; - static final int HIDE_FULLSCREEN = 121; - static final int UPDATE_MATCH_COUNT = 126; - static final int CENTER_FIT_RECT = 127; - static final int SET_SCROLLBAR_MODES = 129; - static final int HIT_TEST_RESULT = 130; - static final int SAVE_WEBARCHIVE_FINISHED = 131; - static final int SET_AUTOFILLABLE = 132; - static final int AUTOFILL_COMPLETE = 133; - static final int SCREEN_ON = 134; - static final int UPDATE_ZOOM_DENSITY = 135; - static final int EXIT_FULLSCREEN_VIDEO = 136; - static final int COPY_TO_CLIPBOARD = 137; - static final int INIT_EDIT_FIELD = 138; - static final int REPLACE_TEXT = 139; - static final int CLEAR_CARET_HANDLE = 140; - static final int KEY_PRESS = 141; - static final int RELOCATE_AUTO_COMPLETE_POPUP = 142; - static final int FOCUS_NODE_CHANGED = 143; - static final int AUTOFILL_FORM = 144; - static final int SCROLL_EDIT_TEXT = 145; - static final int EDIT_TEXT_SIZE_CHANGED = 146; - static final int SHOW_CARET_HANDLE = 147; - static final int UPDATE_CONTENT_BOUNDS = 148; - static final int SCROLL_HANDLE_INTO_VIEW = 149; - - private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; - private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; - - static final String[] HandlerPrivateDebugString = { - "REMEMBER_PASSWORD", // = 1; - "NEVER_REMEMBER_PASSWORD", // = 2; - "SWITCH_TO_SHORTPRESS", // = 3; - "SWITCH_TO_LONGPRESS", // = 4; - "RELEASE_SINGLE_TAP", // = 5; - "REQUEST_FORM_DATA", // = 6; - "RESUME_WEBCORE_PRIORITY", // = 7; - "DRAG_HELD_MOTIONLESS", // = 8; - "", // = 9; - "PREVENT_DEFAULT_TIMEOUT", // = 10; - "SCROLL_SELECT_TEXT" // = 11; - }; - - static final String[] HandlerPackageDebugString = { - "SCROLL_TO_MSG_ID", // = 101; - "102", // = 102; - "103", // = 103; - "104", // = 104; - "NEW_PICTURE_MSG_ID", // = 105; - "UPDATE_TEXT_ENTRY_MSG_ID", // = 106; - "WEBCORE_INITIALIZED_MSG_ID", // = 107; - "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108; - "UPDATE_ZOOM_RANGE", // = 109; - "UNHANDLED_NAV_KEY", // = 110; - "CLEAR_TEXT_ENTRY", // = 111; - "UPDATE_TEXT_SELECTION_MSG_ID", // = 112; - "SHOW_RECT_MSG_ID", // = 113; - "LONG_PRESS_CENTER", // = 114; - "PREVENT_TOUCH_ID", // = 115; - "WEBCORE_NEED_TOUCH_EVENTS", // = 116; - "INVAL_RECT_MSG_ID", // = 117; - "REQUEST_KEYBOARD", // = 118; - "DO_MOTION_UP", // = 119; - "SHOW_FULLSCREEN", // = 120; - "HIDE_FULLSCREEN", // = 121; - "DOM_FOCUS_CHANGED", // = 122; - "REPLACE_BASE_CONTENT", // = 123; - "RETURN_LABEL", // = 125; - "UPDATE_MATCH_COUNT", // = 126; - "CENTER_FIT_RECT", // = 127; - "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128; - "SET_SCROLLBAR_MODES", // = 129; - "SELECTION_STRING_CHANGED", // = 130; - "SET_TOUCH_HIGHLIGHT_RECTS", // = 131; - "SAVE_WEBARCHIVE_FINISHED", // = 132; - "SET_AUTOFILLABLE", // = 133; - "AUTOFILL_COMPLETE", // = 134; - "SELECT_AT", // = 135; - "SCREEN_ON", // = 136; - "ENTER_FULLSCREEN_VIDEO", // = 137; - "UPDATE_SELECTION", // = 138; - "UPDATE_ZOOM_DENSITY" // = 139; - }; - - // If the site doesn't use the viewport meta tag to specify the viewport, - // use DEFAULT_VIEWPORT_WIDTH as the default viewport width - static final int DEFAULT_VIEWPORT_WIDTH = 980; - - // normally we try to fit the content to the minimum preferred width - // calculated by the Webkit. To avoid the bad behavior when some site's - // minimum preferred width keeps growing when changing the viewport width or - // the minimum preferred width is huge, an upper limit is needed. - static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH; - - // initial scale in percent. 0 means using default. - private int mInitialScaleInPercent = 0; - - // Whether or not a scroll event should be sent to webkit. This is only set - // to false when restoring the scroll position. - private boolean mSendScrollEvent = true; - - private int mSnapScrollMode = SNAP_NONE; - private static final int SNAP_NONE = 0; - private static final int SNAP_LOCK = 1; // not a separate state - private static final int SNAP_X = 2; // may be combined with SNAP_LOCK - private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK - private boolean mSnapPositive; - - // keep these in sync with their counterparts in WebView.cpp - private static final int DRAW_EXTRAS_NONE = 0; - private static final int DRAW_EXTRAS_SELECTION = 1; - private static final int DRAW_EXTRAS_CURSOR_RING = 2; - - // keep this in sync with WebCore:ScrollbarMode in WebKit - private static final int SCROLLBAR_AUTO = 0; - private static final int SCROLLBAR_ALWAYSOFF = 1; - // as we auto fade scrollbar, this is ignored. - private static final int SCROLLBAR_ALWAYSON = 2; - private int mHorizontalScrollBarMode = SCROLLBAR_AUTO; - private int mVerticalScrollBarMode = SCROLLBAR_AUTO; - - /** - * Max distance to overscroll by in pixels. - * This how far content can be pulled beyond its normal bounds by the user. - */ - private int mOverscrollDistance; - - /** - * Max distance to overfling by in pixels. - * This is how far flinged content can move beyond the end of its normal bounds. - */ - private int mOverflingDistance; - - private OverScrollGlow mOverScrollGlow; - - // Used to match key downs and key ups - private Vector<Integer> mKeysPressed; - - /* package */ static boolean mLogEvent = true; - - // for event log - private long mLastTouchUpTime = 0; - - private WebViewCore.AutoFillData mAutoFillData; - - private static boolean sNotificationsEnabled = true; - - /** - * URI scheme for telephone number - */ - public static final String SCHEME_TEL = "tel:"; - /** - * URI scheme for email address - */ - public static final String SCHEME_MAILTO = "mailto:"; - /** - * URI scheme for map address - */ - public static final String SCHEME_GEO = "geo:0,0?q="; - - private int mBackgroundColor = Color.WHITE; - - private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second - private int mAutoScrollX = 0; - private int mAutoScrollY = 0; - private int mMinAutoScrollX = 0; - private int mMaxAutoScrollX = 0; - private int mMinAutoScrollY = 0; - private int mMaxAutoScrollY = 0; - private Rect mScrollingLayerBounds = new Rect(); - private boolean mSentAutoScrollMessage = false; - - // used for serializing asynchronously handled touch events. - private WebViewInputDispatcher mInputDispatcher; - - // Used to track whether picture updating was paused due to a window focus change. - private boolean mPictureUpdatePausedForFocusChange = false; - - // Used to notify listeners of a new picture. - private PictureListener mPictureListener; - - // Used to notify listeners about find-on-page results. - private WebView.FindListener mFindListener; - - // Used to prevent resending save password message - private Message mResumeMsg; - - /** - * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information - */ - static class FocusNodeHref { - static final String TITLE = "title"; - static final String URL = "url"; - static final String SRC = "src"; - } - - public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) { - mWebView = webView; - mWebViewPrivate = privateAccess; - mContext = webView.getContext(); - } - - /** - * See {@link WebViewProvider#init(Map, boolean)} - */ - @Override - public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) { - Context context = mContext; - - // Used by the chrome stack to find application paths - JniUtil.setContext(context); - - mCallbackProxy = new CallbackProxy(context, this); - mViewManager = new ViewManager(this); - L10nUtils.setApplicationContext(context.getApplicationContext()); - mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces); - mDatabase = WebViewDatabaseClassic.getInstance(context); - mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel - mZoomManager = new ZoomManager(this, mCallbackProxy); - - /* The init method must follow the creation of certain member variables, - * such as the mZoomManager. - */ - init(); - setupPackageListener(context); - setupProxyListener(context); - setupTrustStorageListener(context); - updateMultiTouchSupport(context); - - if (privateBrowsing) { - startPrivateBrowsing(); - } - - mAutoFillData = new WebViewCore.AutoFillData(); - mEditTextScroller = new Scroller(context); - - // Calculate channel distance - calculateChannelDistance(context); - } - - /** - * Calculate sChannelDistance based on the screen information. - * @param context A Context object used to access application assets. - */ - private void calculateChannelDistance(Context context) { - // The channel distance is adjusted for density and screen size - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - final double screenSize = Math.hypot((double)(metrics.widthPixels/metrics.densityDpi), - (double)(metrics.heightPixels/metrics.densityDpi)); - if (screenSize < 3.0) { - sChannelDistance = 16; - } else if (screenSize < 5.0) { - sChannelDistance = 22; - } else if (screenSize < 7.0) { - sChannelDistance = 28; - } else { - sChannelDistance = 34; - } - sChannelDistance = (int)(sChannelDistance * metrics.density); - if (sChannelDistance < 16) sChannelDistance = 16; - - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "sChannelDistance : " + sChannelDistance - + ", density : " + metrics.density - + ", screenSize : " + screenSize - + ", metrics.heightPixels : " + metrics.heightPixels - + ", metrics.widthPixels : " + metrics.widthPixels - + ", metrics.densityDpi : " + metrics.densityDpi); - } - } - - // WebViewProvider bindings - - static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics { - Factory() { - // Touch JniUtil and WebViewCore in case this is being called from - // WebViewFactory.Preloader, to ensure that the JNI libraries that they use are - // preloaded in the zygote. - try { - Class.forName("android.webkit.JniUtil"); - Class.forName("android.webkit.WebViewCore"); - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "failed to load JNI libraries"); - throw new AndroidRuntimeException(e); - } - } - - @Override - public String findAddress(String addr) { - return WebViewClassic.findAddress(addr); - } - @Override - public void setPlatformNotificationsEnabled(boolean enable) { - if (enable) { - WebViewClassic.enablePlatformNotifications(); - } else { - WebViewClassic.disablePlatformNotifications(); - } - } - - @Override - public Statics getStatics() { return this; } - - @Override - public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { - return new WebViewClassic(webView, privateAccess); - } - - @Override - public GeolocationPermissions getGeolocationPermissions() { - return GeolocationPermissionsClassic.getInstance(); - } - - @Override - public CookieManager getCookieManager() { - return CookieManagerClassic.getInstance(); - } - - @Override - public WebIconDatabase getWebIconDatabase() { - return WebIconDatabaseClassic.getInstance(); - } - - @Override - public WebStorage getWebStorage() { - return WebStorageClassic.getInstance(); - } - - @Override - public WebViewDatabase getWebViewDatabase(Context context) { - return WebViewDatabaseClassic.getInstance(context); - } - - @Override - public String getDefaultUserAgent(Context context) { - return WebSettingsClassic.getDefaultUserAgentForLocale(context, - Locale.getDefault()); - } - - @Override - public void setWebContentsDebuggingEnabled(boolean enable) { - // no-op for WebViewClassic. - } - } - - private void onHandleUiEvent(MotionEvent event, int eventType, int flags) { - switch (eventType) { - case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS: - HitTestResult hitTest = getHitTestResult(); - if (hitTest != null) { - mWebView.performLongClick(); - } - break; - case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP: - mZoomManager.handleDoubleTap(event.getX(), event.getY()); - break; - case WebViewInputDispatcher.EVENT_TYPE_TOUCH: - onHandleUiTouchEvent(event); - break; - case WebViewInputDispatcher.EVENT_TYPE_CLICK: - if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) { - mWebView.playSoundEffect(SoundEffectConstants.CLICK); - overrideLoading(mFocusedNode.mIntentUrl); - } - break; - } - } - - private void onHandleUiTouchEvent(MotionEvent ev) { - final ScaleGestureDetector detector = - mZoomManager.getScaleGestureDetector(); - - int action = ev.getActionMasked(); - final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; - final boolean configChanged = - action == MotionEvent.ACTION_POINTER_UP || - action == MotionEvent.ACTION_POINTER_DOWN; - final int skipIndex = pointerUp ? ev.getActionIndex() : -1; - - // Determine focal point - float sumX = 0, sumY = 0; - final int count = ev.getPointerCount(); - for (int i = 0; i < count; i++) { - if (skipIndex == i) continue; - sumX += ev.getX(i); - sumY += ev.getY(i); - } - final int div = pointerUp ? count - 1 : count; - float x = sumX / div; - float y = sumY / div; - - if (configChanged) { - mLastTouchX = Math.round(x); - mLastTouchY = Math.round(y); - mLastTouchTime = ev.getEventTime(); - mWebView.cancelLongPress(); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - } - - if (detector != null) { - detector.onTouchEvent(ev); - if (detector.isInProgress()) { - mLastTouchTime = ev.getEventTime(); - - if (!mZoomManager.supportsPanDuringZoom()) { - return; - } - mTouchMode = TOUCH_DRAG_MODE; - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - } - } - - if (action == MotionEvent.ACTION_POINTER_DOWN) { - cancelTouch(); - action = MotionEvent.ACTION_DOWN; - } else if (action == MotionEvent.ACTION_MOVE) { - // negative x or y indicate it is on the edge, skip it. - if (x < 0 || y < 0) { - return; - } - } - - handleTouchEventCommon(ev, action, Math.round(x), Math.round(y)); - } - - // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying - // as the first param in the WebViewClient and WebChromeClient callbacks. - final private WebView mWebView; - // Callback interface, provides priviledged access into the WebView instance. - final private WebView.PrivateAccess mWebViewPrivate; - // Cached reference to mWebView.getContext(), for convenience. - final private Context mContext; - - /** - * @return The webview proxy that this classic webview is bound to. - */ - public WebView getWebView() { - return mWebView; - } - - @Override - public ViewDelegate getViewDelegate() { - return this; - } - - @Override - public ScrollDelegate getScrollDelegate() { - return this; - } - - public static WebViewClassic fromWebView(WebView webView) { - return webView == null ? null : (WebViewClassic) webView.getWebViewProvider(); - } - - // Accessors, purely for convenience (and to reduce code churn during webview proxy migration). - int getScrollX() { - return mWebView.getScrollX(); - } - - int getScrollY() { - return mWebView.getScrollY(); - } - - int getWidth() { - return mWebView.getWidth(); - } - - int getHeight() { - return mWebView.getHeight(); - } - - Context getContext() { - return mContext; - } - - void invalidate() { - mWebView.invalidate(); - } - - // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths. - void setScrollXRaw(int mScrollX) { - mWebViewPrivate.setScrollXRaw(mScrollX); - } - - void setScrollYRaw(int mScrollY) { - mWebViewPrivate.setScrollYRaw(mScrollY); - } - - private static class TrustStorageListener extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { - handleCertTrustChanged(); - } - } - } - private static TrustStorageListener sTrustStorageListener; - - /** - * Handles update to the trust storage. - */ - private static void handleCertTrustChanged() { - // send a message for indicating trust storage change - WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null); - } - - /* - * @param context This method expects this to be a valid context. - */ - private static void setupTrustStorageListener(Context context) { - if (sTrustStorageListener != null ) { - return; - } - IntentFilter filter = new IntentFilter(); - filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); - sTrustStorageListener = new TrustStorageListener(); - Intent current = - context.getApplicationContext().registerReceiver(sTrustStorageListener, filter); - if (current != null) { - handleCertTrustChanged(); - } - } - - private static class ProxyReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) { - handleProxyBroadcast(intent); - } - } - } - - /* - * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts. - */ - private static ProxyReceiver sProxyReceiver; - - /* - * @param context This method expects this to be a valid context - */ - private static synchronized void setupProxyListener(Context context) { - if (sProxyReceiver != null || sNotificationsEnabled == false) { - return; - } - IntentFilter filter = new IntentFilter(); - filter.addAction(Proxy.PROXY_CHANGE_ACTION); - sProxyReceiver = new ProxyReceiver(); - Intent currentProxy = context.getApplicationContext().registerReceiver( - sProxyReceiver, filter); - if (currentProxy != null) { - handleProxyBroadcast(currentProxy); - } - } - - /* - * @param context This method expects this to be a valid context - */ - private static synchronized void disableProxyListener(Context context) { - if (sProxyReceiver == null) - return; - - context.getApplicationContext().unregisterReceiver(sProxyReceiver); - sProxyReceiver = null; - } - - private static void handleProxyBroadcast(Intent intent) { - ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO); - if (proxyProperties == null || proxyProperties.getHost() == null) { - WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null); - return; - } - WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties); - } - - /* - * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED - * or ACTION_PACKAGE_REMOVED. - */ - private static boolean sPackageInstallationReceiverAdded = false; - - /* - * A set of Google packages we monitor for the - * navigator.isApplicationInstalled() API. Add additional packages as - * needed. - */ - private static Set<String> sGoogleApps; - static { - sGoogleApps = new HashSet<String>(); - sGoogleApps.add("com.google.android.youtube"); - } - - private static class PackageListener extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - final String packageName = intent.getData().getSchemeSpecificPart(); - final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); - if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) { - // if it is replacing, refreshPlugins() when adding - return; - } - - if (sGoogleApps.contains(packageName)) { - if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName); - } else { - WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName); - } - } - - PluginManager pm = PluginManager.getInstance(context); - if (pm.containsPluginPermissionAndSignatures(packageName)) { - pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action)); - } - } - } - - private void setupPackageListener(Context context) { - - /* - * we must synchronize the instance check and the creation of the - * receiver to ensure that only ONE receiver exists for all WebView - * instances. - */ - synchronized (WebViewClassic.class) { - - // if the receiver already exists then we do not need to register it - // again - if (sPackageInstallationReceiverAdded) { - return; - } - - IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_REMOVED); - filter.addDataScheme("package"); - BroadcastReceiver packageListener = new PackageListener(); - context.getApplicationContext().registerReceiver(packageListener, filter); - sPackageInstallationReceiverAdded = true; - } - - // check if any of the monitored apps are already installed - AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() { - - @Override - protected Set<String> doInBackground(Void... unused) { - Set<String> installedPackages = new HashSet<String>(); - PackageManager pm = mContext.getPackageManager(); - for (String name : sGoogleApps) { - try { - pm.getPackageInfo(name, - PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES); - installedPackages.add(name); - } catch (PackageManager.NameNotFoundException e) { - // package not found - } - } - return installedPackages; - } - - // Executes on the UI thread - @Override - protected void onPostExecute(Set<String> installedPackages) { - if (mWebViewCore != null) { - mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages); - } - } - }; - task.execute(); - } - - void updateMultiTouchSupport(Context context) { - mZoomManager.updateMultiTouchSupport(context); - } - - void updateJavaScriptEnabled(boolean enabled) { - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().updateJavaScriptEnabled(enabled); - } - } - - private void init() { - OnTrimMemoryListener.init(mContext); - mWebView.setWillNotDraw(false); - mWebView.setClickable(true); - mWebView.setLongClickable(true); - - final ViewConfiguration configuration = ViewConfiguration.get(mContext); - int slop = configuration.getScaledTouchSlop(); - mTouchSlopSquare = slop * slop; - slop = configuration.getScaledDoubleTapSlop(); - mDoubleTapSlopSquare = slop * slop; - final float density = WebViewCore.getFixedDisplayDensity(mContext); - // use one line height, 16 based on our current default font, for how - // far we allow a touch be away from the edge of a link - mNavSlop = (int) (16 * density); - mZoomManager.init(density); - mMaximumFling = configuration.getScaledMaximumFlingVelocity(); - - // Compute the inverse of the density squared. - DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density); - - mOverscrollDistance = configuration.getScaledOverscrollDistance(); - mOverflingDistance = configuration.getScaledOverflingDistance(); - - setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle()); - // Initially use a size of two, since the user is likely to only hold - // down two keys at a time (shift + another key) - mKeysPressed = new Vector<Integer>(2); - mHTML5VideoViewProxy = null ; - } - - @Override - public boolean shouldDelayChildPressedState() { - return true; - } - - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (!mWebView.isEnabled()) { - // Only default actions are supported while disabled. - return mWebViewPrivate.super_performAccessibilityAction(action, arguments); - } - - if (getAccessibilityInjector().supportsAccessibilityAction(action)) { - return getAccessibilityInjector().performAccessibilityAction(action, arguments); - } - - switch (action) { - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { - final int convertedContentHeight = contentToViewY(getContentHeight()); - final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop() - - mWebView.getPaddingBottom(); - final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0); - final boolean canScrollBackward = (getScrollY() > 0); - final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0); - if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) { - mWebView.scrollBy(0, adjustedViewHeight); - return true; - } - if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) { - mWebView.scrollBy(0, -adjustedViewHeight); - return true; - } - return false; - } - } - - return mWebViewPrivate.super_performAccessibilityAction(action, arguments); - } - - @Override - public AccessibilityNodeProvider getAccessibilityNodeProvider() { - return null; - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - if (!mWebView.isEnabled()) { - // Only default actions are supported while disabled. - return; - } - - info.setScrollable(isScrollableForAccessibility()); - - final int convertedContentHeight = contentToViewY(getContentHeight()); - final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop() - - mWebView.getPaddingBottom(); - final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0); - final boolean canScrollBackward = (getScrollY() > 0); - final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0); - - if (canScrollForward) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); - } - - if (canScrollForward) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); - } - - getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info); - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - event.setScrollable(isScrollableForAccessibility()); - event.setScrollX(getScrollX()); - event.setScrollY(getScrollY()); - final int convertedContentWidth = contentToViewX(getContentWidth()); - final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft() - - mWebView.getPaddingLeft(); - event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0)); - final int convertedContentHeight = contentToViewY(getContentHeight()); - final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop() - - mWebView.getPaddingBottom(); - event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0)); - } - - /* package */ void handleSelectionChangedWebCoreThread(String selection, int token) { - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().onSelectionStringChangedWebCoreThread(selection, token); - } - } - - private boolean isAccessibilityInjectionEnabled() { - final AccessibilityManager manager = AccessibilityManager.getInstance(mContext); - if (!manager.isEnabled()) { - return false; - } - - // Accessibility scripts should be injected only when a speaking service - // is enabled. This may need to change later to accommodate Braille. - final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_SPOKEN); - if (services.isEmpty()) { - return false; - } - - return true; - } - - private AccessibilityInjector getAccessibilityInjector() { - if (mAccessibilityInjector == null) { - mAccessibilityInjector = new AccessibilityInjector(this); - } - return mAccessibilityInjector; - } - - private boolean isScrollableForAccessibility() { - return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft() - - mWebView.getPaddingRight() - || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop() - - mWebView.getPaddingBottom()); - } - - @Override - public void setOverScrollMode(int mode) { - if (mode != View.OVER_SCROLL_NEVER) { - if (mOverScrollGlow == null) { - mOverScrollGlow = new OverScrollGlow(this); - } - } else { - mOverScrollGlow = null; - } - } - - /* package */ void adjustDefaultZoomDensity(int zoomDensity) { - final float density = WebViewCore.getFixedDisplayDensity(mContext) - * 100 / zoomDensity; - updateDefaultZoomDensity(density); - } - - /* package */ void updateDefaultZoomDensity(float density) { - mNavSlop = (int) (16 * density); - mZoomManager.updateDefaultZoomDensity(density); - } - - /* package */ int getScaledNavSlop() { - return viewToContentDimension(mNavSlop); - } - - /* package */ boolean onSavePassword(String schemePlusHost, String username, - String password, final Message resumeMsg) { - boolean rVal = false; - if (resumeMsg == null) { - // null resumeMsg implies saving password silently - mDatabase.setUsernamePassword(schemePlusHost, username, password); - } else { - if (mResumeMsg != null) { - Log.w(LOGTAG, "onSavePassword should not be called while dialog is up"); - resumeMsg.sendToTarget(); - return true; - } - mResumeMsg = resumeMsg; - final Message remember = mPrivateHandler.obtainMessage( - REMEMBER_PASSWORD); - remember.getData().putString("host", schemePlusHost); - remember.getData().putString("username", username); - remember.getData().putString("password", password); - remember.obj = resumeMsg; - - final Message neverRemember = mPrivateHandler.obtainMessage( - NEVER_REMEMBER_PASSWORD); - neverRemember.getData().putString("host", schemePlusHost); - neverRemember.getData().putString("username", username); - neverRemember.getData().putString("password", password); - neverRemember.obj = resumeMsg; - - mSavePasswordDialog = new AlertDialog.Builder(mContext) - .setTitle(com.android.internal.R.string.save_password_label) - .setMessage(com.android.internal.R.string.save_password_message) - .setPositiveButton(com.android.internal.R.string.save_password_notnow, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (mResumeMsg != null) { - resumeMsg.sendToTarget(); - mResumeMsg = null; - } - mSavePasswordDialog = null; - } - }) - .setNeutralButton(com.android.internal.R.string.save_password_remember, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (mResumeMsg != null) { - remember.sendToTarget(); - mResumeMsg = null; - } - mSavePasswordDialog = null; - } - }) - .setNegativeButton(com.android.internal.R.string.save_password_never, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (mResumeMsg != null) { - neverRemember.sendToTarget(); - mResumeMsg = null; - } - mSavePasswordDialog = null; - } - }) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - if (mResumeMsg != null) { - resumeMsg.sendToTarget(); - mResumeMsg = null; - } - mSavePasswordDialog = null; - } - }).show(); - // Return true so that WebViewCore will pause while the dialog is - // up. - rVal = true; - } - return rVal; - } - - @Override - public void setScrollBarStyle(int style) { - if (style == View.SCROLLBARS_INSIDE_INSET - || style == View.SCROLLBARS_OUTSIDE_INSET) { - mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false; - } else { - mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true; - } - } - - /** - * See {@link WebView#setHorizontalScrollbarOverlay(boolean)} - */ - @Override - public void setHorizontalScrollbarOverlay(boolean overlay) { - mOverlayHorizontalScrollbar = overlay; - } - - /** - * See {@link WebView#setVerticalScrollbarOverlay(boolean) - */ - @Override - public void setVerticalScrollbarOverlay(boolean overlay) { - mOverlayVerticalScrollbar = overlay; - } - - /** - * See {@link WebView#overlayHorizontalScrollbar()} - */ - @Override - public boolean overlayHorizontalScrollbar() { - return mOverlayHorizontalScrollbar; - } - - /** - * See {@link WebView#overlayVerticalScrollbar()} - */ - @Override - public boolean overlayVerticalScrollbar() { - return mOverlayVerticalScrollbar; - } - - /* - * Return the width of the view where the content of WebView should render - * to. - * Note: this can be called from WebCoreThread. - */ - /* package */ int getViewWidth() { - if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) { - return getWidth(); - } else { - return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth()); - } - } - - // Interface to enable the browser to override title bar handling. - public interface TitleBarDelegate { - int getTitleHeight(); - public void onSetEmbeddedTitleBar(final View title); - } - - /** - * Returns the height (in pixels) of the embedded title bar (if any). Does not care about - * scrolling - */ - protected int getTitleHeight() { - if (mWebView instanceof TitleBarDelegate) { - return ((TitleBarDelegate) mWebView).getTitleHeight(); - } - return 0; - } - - /** - * See {@link WebView#getVisibleTitleHeight()} - */ - @Override - @Deprecated - public int getVisibleTitleHeight() { - // Actually, this method returns the height of the embedded title bar if one is set via the - // hidden setEmbeddedTitleBar method. - return getVisibleTitleHeightImpl(); - } - - private int getVisibleTitleHeightImpl() { - // need to restrict mScrollY due to over scroll - return Math.max(getTitleHeight() - Math.max(0, getScrollY()), - getOverlappingActionModeHeight()); - } - - private int mCachedOverlappingActionModeHeight = -1; - - private int getOverlappingActionModeHeight() { - if (mFindCallback == null) { - return 0; - } - if (mCachedOverlappingActionModeHeight < 0) { - mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset); - mCachedOverlappingActionModeHeight = Math.max(0, - mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top); - } - return mCachedOverlappingActionModeHeight; - } - - /* - * Return the height of the view where the content of WebView should render - * to. Note that this excludes mTitleBar, if there is one. - * Note: this can be called from WebCoreThread. - */ - /* package */ int getViewHeight() { - return getViewHeightWithTitle() - getVisibleTitleHeightImpl(); - } - - int getViewHeightWithTitle() { - int height = getHeight(); - if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) { - height -= mWebViewPrivate.getHorizontalScrollbarHeight(); - } - return height; - } - - /** - * See {@link WebView#getCertificate()} - */ - @Override - public SslCertificate getCertificate() { - return mCertificate; - } - - /** - * See {@link WebView#setCertificate(SslCertificate)} - */ - @Override - public void setCertificate(SslCertificate certificate) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "setCertificate=" + certificate); - } - // here, the certificate can be null (if the site is not secure) - mCertificate = certificate; - } - - //------------------------------------------------------------------------- - // Methods called by activity - //------------------------------------------------------------------------- - - /** - * See {@link WebView#savePassword(String, String, String)} - */ - @Override - public void savePassword(String host, String username, String password) { - mDatabase.setUsernamePassword(host, username, password); - } - - /** - * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)} - */ - @Override - public void setHttpAuthUsernamePassword(String host, String realm, - String username, String password) { - mDatabase.setHttpAuthUsernamePassword(host, realm, username, password); - } - - /** - * See {@link WebView#getHttpAuthUsernamePassword(String, String)} - */ - @Override - public String[] getHttpAuthUsernamePassword(String host, String realm) { - return mDatabase.getHttpAuthUsernamePassword(host, realm); - } - - /** - * Remove Find or Select ActionModes, if active. - */ - private void clearActionModes() { - if (mSelectCallback != null) { - mSelectCallback.finish(); - } - if (mFindCallback != null) { - mFindCallback.finish(); - } - } - - /** - * Called to clear state when moving from one page to another, or changing - * in some other way that makes elements associated with the current page - * (such as ActionModes) no longer relevant. - */ - private void clearHelpers() { - hideSoftKeyboard(); - clearActionModes(); - dismissFullScreenMode(); - cancelDialogs(); - } - - private void cancelDialogs() { - if (mListBoxDialog != null) { - mListBoxDialog.cancel(); - mListBoxDialog = null; - } - if (mSavePasswordDialog != null) { - mSavePasswordDialog.dismiss(); - mSavePasswordDialog = null; - } - } - - /** - * See {@link WebView#destroy()} - */ - @Override - public void destroy() { - if (mWebView.getViewRootImpl() != null) { - Log.e(LOGTAG, Log.getStackTraceString( - new Throwable("Error: WebView.destroy() called while still attached!"))); - } - ensureFunctorDetached(); - destroyJava(); - destroyNative(); - } - - private void ensureFunctorDetached() { - if (mWebView.isHardwareAccelerated()) { - int drawGLFunction = nativeGetDrawGLFunction(mNativeClass); - ViewRootImpl viewRoot = mWebView.getViewRootImpl(); - if (drawGLFunction != 0 && viewRoot != null) { - viewRoot.detachFunctor(drawGLFunction); - } - } - } - - private void destroyJava() { - mCallbackProxy.blockMessages(); - if (mAccessibilityInjector != null) { - mAccessibilityInjector.destroy(); - mAccessibilityInjector = null; - } - if (mWebViewCore != null) { - // Tell WebViewCore to destroy itself - synchronized (this) { - WebViewCore webViewCore = mWebViewCore; - mWebViewCore = null; // prevent using partial webViewCore - webViewCore.destroy(); - } - // Remove any pending messages that might not be serviced yet. - mPrivateHandler.removeCallbacksAndMessages(null); - } - } - - private void destroyNative() { - if (mNativeClass == 0) return; - int nptr = mNativeClass; - mNativeClass = 0; - if (Thread.currentThread() == mPrivateHandler.getLooper().getThread()) { - // We are on the main thread and can safely delete - nativeDestroy(nptr); - } else { - mPrivateHandler.post(new DestroyNativeRunnable(nptr)); - } - } - - private static class DestroyNativeRunnable implements Runnable { - - private int mNativePtr; - - public DestroyNativeRunnable(int nativePtr) { - mNativePtr = nativePtr; - } - - @Override - public void run() { - // nativeDestroy also does a stopGL() - nativeDestroy(mNativePtr); - } - - } - - /** - * See {@link WebView#enablePlatformNotifications()} - */ - @Deprecated - public static void enablePlatformNotifications() { - synchronized (WebViewClassic.class) { - sNotificationsEnabled = true; - Context context = JniUtil.getContext(); - if (context != null) - setupProxyListener(context); - } - } - - /** - * See {@link WebView#disablePlatformNotifications()} - */ - @Deprecated - public static void disablePlatformNotifications() { - synchronized (WebViewClassic.class) { - sNotificationsEnabled = false; - Context context = JniUtil.getContext(); - if (context != null) - disableProxyListener(context); - } - } - - /** - * Sets JavaScript engine flags. - * - * @param flags JS engine flags in a String - * - * This is an implementation detail. - */ - public void setJsFlags(String flags) { - mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags); - } - - /** - * See {@link WebView#setNetworkAvailable(boolean)} - */ - @Override - public void setNetworkAvailable(boolean networkUp) { - mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE, - networkUp ? 1 : 0, 0); - } - - /** - * Inform WebView about the current network type. - */ - public void setNetworkType(String type, String subtype) { - Map<String, String> map = new HashMap<String, String>(); - map.put("type", type); - map.put("subtype", subtype); - mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map); - } - - /** - * See {@link WebView#saveState(Bundle)} - */ - @Override - public WebBackForwardList saveState(Bundle outState) { - if (outState == null) { - return null; - } - // We grab a copy of the back/forward list because a client of WebView - // may have invalidated the history list by calling clearHistory. - WebBackForwardListClassic list = copyBackForwardList(); - final int currentIndex = list.getCurrentIndex(); - final int size = list.getSize(); - // We should fail saving the state if the list is empty or the index is - // not in a valid range. - if (currentIndex < 0 || currentIndex >= size || size == 0) { - return null; - } - outState.putInt("index", currentIndex); - // FIXME: This should just be a byte[][] instead of ArrayList but - // Parcel.java does not have the code to handle multi-dimensional - // arrays. - ArrayList<byte[]> history = new ArrayList<byte[]>(size); - for (int i = 0; i < size; i++) { - WebHistoryItemClassic item = list.getItemAtIndex(i); - if (null == item) { - // FIXME: this shouldn't happen - // need to determine how item got set to null - Log.w(LOGTAG, "saveState: Unexpected null history item."); - return null; - } - byte[] data = item.getFlattenedData(); - if (data == null) { - // It would be very odd to not have any data for a given history - // item. And we will fail to rebuild the history list without - // flattened data. - return null; - } - history.add(data); - } - outState.putSerializable("history", history); - if (mCertificate != null) { - outState.putBundle("certificate", - SslCertificate.saveState(mCertificate)); - } - outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled()); - mZoomManager.saveZoomState(outState); - return list; - } - - /** - * See {@link WebView#savePicture(Bundle, File)} - */ - @Override - @Deprecated - public boolean savePicture(Bundle b, final File dest) { - if (dest == null || b == null) { - return false; - } - final Picture p = capturePicture(); - // Use a temporary file while writing to ensure the destination file - // contains valid data. - final File temp = new File(dest.getPath() + ".writing"); - new Thread(new Runnable() { - @Override - public void run() { - FileOutputStream out = null; - try { - out = new FileOutputStream(temp); - p.writeToStream(out); - // Writing the picture succeeded, rename the temporary file - // to the destination. - temp.renameTo(dest); - } catch (Exception e) { - // too late to do anything about it. - } finally { - if (out != null) { - try { - out.close(); - } catch (Exception e) { - // Can't do anything about that - } - } - temp.delete(); - } - } - }).start(); - // now update the bundle - b.putInt("scrollX", getScrollX()); - b.putInt("scrollY", getScrollY()); - mZoomManager.saveZoomState(b); - return true; - } - - private void restoreHistoryPictureFields(Picture p, Bundle b) { - int sx = b.getInt("scrollX", 0); - int sy = b.getInt("scrollY", 0); - - mDrawHistory = true; - mHistoryPicture = p; - - setScrollXRaw(sx); - setScrollYRaw(sy); - mZoomManager.restoreZoomState(b); - final float scale = mZoomManager.getScale(); - mHistoryWidth = Math.round(p.getWidth() * scale); - mHistoryHeight = Math.round(p.getHeight() * scale); - - invalidate(); - } - - /** - * See {@link WebView#restorePicture(Bundle, File)}; - */ - @Override - @Deprecated - public boolean restorePicture(Bundle b, File src) { - if (src == null || b == null) { - return false; - } - if (!src.exists()) { - return false; - } - try { - final FileInputStream in = new FileInputStream(src); - final Bundle copy = new Bundle(b); - new Thread(new Runnable() { - @Override - public void run() { - try { - final Picture p = Picture.createFromStream(in); - if (p != null) { - // Post a runnable on the main thread to update the - // history picture fields. - mPrivateHandler.post(new Runnable() { - @Override - public void run() { - restoreHistoryPictureFields(p, copy); - } - }); - } - } finally { - try { - in.close(); - } catch (Exception e) { - // Nothing we can do now. - } - } - } - }).start(); - } catch (FileNotFoundException e){ - e.printStackTrace(); - } - return true; - } - - /** - * Saves the view data to the output stream. The output is highly - * version specific, and may not be able to be loaded by newer versions - * of WebView. - * @param stream The {@link OutputStream} to save to - * @param callback The {@link ValueCallback} to call with the result - */ - public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) { - if (mWebViewCore == null) { - callback.onReceiveValue(false); - return; - } - mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE, - new WebViewCore.SaveViewStateRequest(stream, callback)); - } - - /** - * Loads the view data from the input stream. See - * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information. - * @param stream The {@link InputStream} to load from - */ - public void loadViewState(InputStream stream) { - mBlockWebkitViewMessages = true; - new AsyncTask<InputStream, Void, DrawData>() { - - @Override - protected DrawData doInBackground(InputStream... params) { - try { - return ViewStateSerializer.deserializeViewState(params[0]); - } catch (IOException e) { - return null; - } - } - - @Override - protected void onPostExecute(DrawData draw) { - if (draw == null) { - Log.e(LOGTAG, "Failed to load view state!"); - return; - } - int viewWidth = getViewWidth(); - int viewHeight = getViewHeightWithTitle() - getTitleHeight(); - draw.mViewSize = new Point(viewWidth, viewHeight); - draw.mViewState.mDefaultScale = getDefaultZoomScale(); - mLoadedPicture = draw; - setNewPicture(mLoadedPicture, true); - mLoadedPicture.mViewState = null; - } - - }.execute(stream); - } - - /** - * Clears the view state set with {@link #loadViewState(InputStream)}. - * This WebView will then switch to showing the content from webkit - */ - public void clearViewState() { - mBlockWebkitViewMessages = false; - mLoadedPicture = null; - invalidate(); - } - - /** - * See {@link WebView#restoreState(Bundle)} - */ - @Override - public WebBackForwardList restoreState(Bundle inState) { - WebBackForwardListClassic returnList = null; - if (inState == null) { - return returnList; - } - if (inState.containsKey("index") && inState.containsKey("history")) { - mCertificate = SslCertificate.restoreState( - inState.getBundle("certificate")); - - final WebBackForwardListClassic list = mCallbackProxy.getBackForwardList(); - final int index = inState.getInt("index"); - // We can't use a clone of the list because we need to modify the - // shared copy, so synchronize instead to prevent concurrent - // modifications. - synchronized (list) { - final List<byte[]> history = - (List<byte[]>) inState.getSerializable("history"); - final int size = history.size(); - // Check the index bounds so we don't crash in native code while - // restoring the history index. - if (index < 0 || index >= size) { - return null; - } - for (int i = 0; i < size; i++) { - byte[] data = history.remove(0); - if (data == null) { - // If we somehow have null data, we cannot reconstruct - // the item and thus our history list cannot be rebuilt. - return null; - } - WebHistoryItem item = new WebHistoryItemClassic(data); - list.addHistoryItem(item); - } - // Grab the most recent copy to return to the caller. - returnList = copyBackForwardList(); - // Update the copy to have the correct index. - returnList.setCurrentIndex(index); - } - // Restore private browsing setting. - if (inState.getBoolean("privateBrowsingEnabled")) { - getSettings().setPrivateBrowsingEnabled(true); - } - mZoomManager.restoreZoomState(inState); - // Remove all pending messages because we are restoring previous - // state. - mWebViewCore.removeMessages(); - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().addAccessibilityApisIfNecessary(); - } - // Send a restore state message. - mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index); - } - return returnList; - } - - /** - * See {@link WebView#loadUrl(String, Map)} - */ - @Override - public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { - loadUrlImpl(url, additionalHttpHeaders); - } - - private void loadUrlImpl(String url, Map<String, String> extraHeaders) { - switchOutDrawHistory(); - WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData(); - arg.mUrl = url; - arg.mExtraHeaders = extraHeaders; - mWebViewCore.sendMessage(EventHub.LOAD_URL, arg); - clearHelpers(); - } - - /** - * See {@link WebView#loadUrl(String)} - */ - @Override - public void loadUrl(String url) { - loadUrlImpl(url); - } - - private void loadUrlImpl(String url) { - if (url == null) { - return; - } - loadUrlImpl(url, null); - } - - /** - * See {@link WebView#postUrl(String, byte[])} - */ - @Override - public void postUrl(String url, byte[] postData) { - if (URLUtil.isNetworkUrl(url)) { - switchOutDrawHistory(); - WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData(); - arg.mUrl = url; - arg.mPostData = postData; - mWebViewCore.sendMessage(EventHub.POST_URL, arg); - clearHelpers(); - } else { - loadUrlImpl(url); - } - } - - /** - * See {@link WebView#loadData(String, String, String)} - */ - @Override - public void loadData(String data, String mimeType, String encoding) { - loadDataImpl(data, mimeType, encoding); - } - - private void loadDataImpl(String data, String mimeType, String encoding) { - StringBuilder dataUrl = new StringBuilder("data:"); - dataUrl.append(mimeType); - if ("base64".equals(encoding)) { - dataUrl.append(";base64"); - } - dataUrl.append(","); - dataUrl.append(data); - loadUrlImpl(dataUrl.toString()); - } - - /** - * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)} - */ - @Override - public void loadDataWithBaseURL(String baseUrl, String data, - String mimeType, String encoding, String historyUrl) { - - if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) { - loadDataImpl(data, mimeType, encoding); - return; - } - switchOutDrawHistory(); - WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData(); - arg.mBaseUrl = baseUrl; - arg.mData = data; - arg.mMimeType = mimeType; - arg.mEncoding = encoding; - arg.mHistoryUrl = historyUrl; - mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg); - clearHelpers(); - } - - @Override - public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) { - // K-only API not implemented in WebViewClassic. - throw new IllegalStateException("This API not supported on Android 4.3 and earlier"); - } - - /** - * See {@link WebView#saveWebArchive(String)} - */ - @Override - public void saveWebArchive(String filename) { - saveWebArchiveImpl(filename, false, null); - } - - /* package */ static class SaveWebArchiveMessage { - SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) { - mBasename = basename; - mAutoname = autoname; - mCallback = callback; - } - - /* package */ final String mBasename; - /* package */ final boolean mAutoname; - /* package */ final ValueCallback<String> mCallback; - /* package */ String mResultFile; - } - - /** - * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)} - */ - @Override - public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) { - saveWebArchiveImpl(basename, autoname, callback); - } - - private void saveWebArchiveImpl(String basename, boolean autoname, - ValueCallback<String> callback) { - mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE, - new SaveWebArchiveMessage(basename, autoname, callback)); - } - - /** - * See {@link WebView#stopLoading()} - */ - @Override - public void stopLoading() { - // TODO: should we clear all the messages in the queue before sending - // STOP_LOADING? - switchOutDrawHistory(); - mWebViewCore.sendMessage(EventHub.STOP_LOADING); - } - - /** - * See {@link WebView#reload()} - */ - @Override - public void reload() { - clearHelpers(); - switchOutDrawHistory(); - mWebViewCore.sendMessage(EventHub.RELOAD); - } - - /** - * See {@link WebView#canGoBack()} - */ - @Override - public boolean canGoBack() { - WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); - synchronized (l) { - if (l.getClearPending()) { - return false; - } else { - return l.getCurrentIndex() > 0; - } - } - } - - /** - * See {@link WebView#goBack()} - */ - @Override - public void goBack() { - goBackOrForwardImpl(-1); - } - - /** - * See {@link WebView#canGoForward()} - */ - @Override - public boolean canGoForward() { - WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); - synchronized (l) { - if (l.getClearPending()) { - return false; - } else { - return l.getCurrentIndex() < l.getSize() - 1; - } - } - } - - /** - * See {@link WebView#goForward()} - */ - @Override - public void goForward() { - goBackOrForwardImpl(1); - } - - /** - * See {@link WebView#canGoBackOrForward(int)} - */ - @Override - public boolean canGoBackOrForward(int steps) { - WebBackForwardListClassic l = mCallbackProxy.getBackForwardList(); - synchronized (l) { - if (l.getClearPending()) { - return false; - } else { - int newIndex = l.getCurrentIndex() + steps; - return newIndex >= 0 && newIndex < l.getSize(); - } - } - } - - /** - * See {@link WebView#goBackOrForward(int)} - */ - @Override - public void goBackOrForward(int steps) { - goBackOrForwardImpl(steps); - } - - private void goBackOrForwardImpl(int steps) { - goBackOrForward(steps, false); - } - - private void goBackOrForward(int steps, boolean ignoreSnapshot) { - if (steps != 0) { - clearHelpers(); - mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps, - ignoreSnapshot ? 1 : 0); - } - } - - /** - * See {@link WebView#isPrivateBrowsingEnabled()} - */ - @Override - public boolean isPrivateBrowsingEnabled() { - WebSettingsClassic settings = getSettings(); - return (settings != null) ? settings.isPrivateBrowsingEnabled() : false; - } - - private void startPrivateBrowsing() { - getSettings().setPrivateBrowsingEnabled(true); - } - - private boolean extendScroll(int y) { - int finalY = mScroller.getFinalY(); - int newY = pinLocY(finalY + y); - if (newY == finalY) return false; - mScroller.setFinalY(newY); - mScroller.extendDuration(computeDuration(0, y)); - return true; - } - - /** - * See {@link WebView#pageUp(boolean)} - */ - @Override - public boolean pageUp(boolean top) { - if (mNativeClass == 0) { - return false; - } - if (top) { - // go to the top of the document - return pinScrollTo(getScrollX(), 0, true, 0); - } - // Page up - int h = getHeight(); - int y; - if (h > 2 * PAGE_SCROLL_OVERLAP) { - y = -h + PAGE_SCROLL_OVERLAP; - } else { - y = -h / 2; - } - return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) - : extendScroll(y); - } - - /** - * See {@link WebView#pageDown(boolean)} - */ - @Override - public boolean pageDown(boolean bottom) { - if (mNativeClass == 0) { - return false; - } - if (bottom) { - return pinScrollTo(getScrollX(), computeRealVerticalScrollRange(), true, 0); - } - // Page down. - int h = getHeight(); - int y; - if (h > 2 * PAGE_SCROLL_OVERLAP) { - y = h - PAGE_SCROLL_OVERLAP; - } else { - y = h / 2; - } - return mScroller.isFinished() ? pinScrollBy(0, y, true, 0) - : extendScroll(y); - } - - /** - * See {@link WebView#clearView()} - */ - @Override - public void clearView() { - mContentWidth = 0; - mContentHeight = 0; - setBaseLayer(0, false, false); - mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT); - } - - /** - * See {@link WebView#capturePicture()} - */ - @Override - public Picture capturePicture() { - if (mNativeClass == 0) return null; - Picture result = new Picture(); - nativeCopyBaseContentToPicture(result); - return result; - } - - /** - * See {@link WebView#createPrintDocumentAdapter()} - */ - @Override - public PrintDocumentAdapter createPrintDocumentAdapter() { - // K-only API not implemented in WebViewClassic. - throw new IllegalStateException("This API not supported on Android 4.3 and earlier"); - } - - /** - * See {@link WebView#getScale()} - */ - @Override - public float getScale() { - return mZoomManager.getScale(); - } - - /** - * Compute the reading level scale of the WebView - * @param scale The current scale. - * @return The reading level scale. - */ - /*package*/ float computeReadingLevelScale(float scale) { - return mZoomManager.computeReadingLevelScale(scale); - } - - /** - * See {@link WebView#setInitialScale(int)} - */ - @Override - public void setInitialScale(int scaleInPercent) { - mZoomManager.setInitialScaleInPercent(scaleInPercent); - } - - /** - * See {@link WebView#invokeZoomPicker()} - */ - @Override - public void invokeZoomPicker() { - if (!getSettings().supportZoom()) { - Log.w(LOGTAG, "This WebView doesn't support zoom."); - return; - } - clearHelpers(); - mZoomManager.invokeZoomPicker(); - } - - /** - * See {@link WebView#getHitTestResult()} - */ - @Override - public HitTestResult getHitTestResult() { - return mInitialHitTestResult; - } - - // No left edge for double-tap zoom alignment - static final int NO_LEFTEDGE = -1; - - int getBlockLeftEdge(int x, int y, float readingScale) { - float invReadingScale = 1.0f / readingScale; - int readingWidth = (int) (getViewWidth() * invReadingScale); - int left = NO_LEFTEDGE; - if (mFocusedNode != null) { - final int length = mFocusedNode.mEnclosingParentRects.length; - for (int i = 0; i < length; i++) { - Rect rect = mFocusedNode.mEnclosingParentRects[i]; - if (rect.width() < mFocusedNode.mHitTestSlop) { - // ignore bounding boxes that are too small - continue; - } else if (rect.width() > readingWidth) { - // stop when bounding box doesn't fit the screen width - // at reading scale - break; - } - - left = rect.left; - } - } - - return left; - } - - /** - * See {@link WebView#requestFocusNodeHref(Message)} - */ - @Override - public void requestFocusNodeHref(Message hrefMsg) { - if (hrefMsg == null) { - return; - } - int contentX = viewToContentX(mLastTouchX + getScrollX()); - int contentY = viewToContentY(mLastTouchY + getScrollY()); - if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX - && mFocusedNode.mHitTestY == contentY) { - hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl); - hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText); - hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl); - hrefMsg.sendToTarget(); - return; - } - mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF, - contentX, contentY, hrefMsg); - } - - /** - * See {@link WebView#requestImageRef(Message)} - */ - @Override - public void requestImageRef(Message msg) { - if (0 == mNativeClass) return; // client isn't initialized - String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null; - Bundle data = msg.getData(); - data.putString("url", url); - msg.setData(data); - msg.sendToTarget(); - } - - static int pinLoc(int x, int viewMax, int docMax) { -// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax); - if (docMax < viewMax) { // the doc has room on the sides for "blank" - // pin the short document to the top/left of the screen - x = 0; -// Log.d(LOGTAG, "--- center " + x); - } else if (x < 0) { - x = 0; -// Log.d(LOGTAG, "--- zero"); - } else if (x + viewMax > docMax) { - x = docMax - viewMax; -// Log.d(LOGTAG, "--- pin " + x); - } - return x; - } - - // Expects x in view coordinates - int pinLocX(int x) { - if (mInOverScrollMode) return x; - return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange()); - } - - // Expects y in view coordinates - int pinLocY(int y) { - if (mInOverScrollMode) return y; - return pinLoc(y, getViewHeightWithTitle(), - computeRealVerticalScrollRange() + getTitleHeight()); - } - - /** - * Given a distance in view space, convert it to content space. Note: this - * does not reflect translation, just scaling, so this should not be called - * with coordinates, but should be called for dimensions like width or - * height. - */ - private int viewToContentDimension(int d) { - return Math.round(d * mZoomManager.getInvScale()); - } - - /** - * Given an x coordinate in view space, convert it to content space. Also - * may be used for absolute heights. - */ - /*package*/ int viewToContentX(int x) { - return viewToContentDimension(x); - } - - /** - * Given a y coordinate in view space, convert it to content space. - * Takes into account the height of the title bar if there is one - * embedded into the WebView. - */ - /*package*/ int viewToContentY(int y) { - return viewToContentDimension(y - getTitleHeight()); - } - - /** - * Given a x coordinate in view space, convert it to content space. - * Returns the result as a float. - */ - private float viewToContentXf(int x) { - return x * mZoomManager.getInvScale(); - } - - /** - * Given a y coordinate in view space, convert it to content space. - * Takes into account the height of the title bar if there is one - * embedded into the WebView. Returns the result as a float. - */ - private float viewToContentYf(int y) { - return (y - getTitleHeight()) * mZoomManager.getInvScale(); - } - - /** - * Given a distance in content space, convert it to view space. Note: this - * does not reflect translation, just scaling, so this should not be called - * with coordinates, but should be called for dimensions like width or - * height. - */ - /*package*/ int contentToViewDimension(int d) { - return Math.round(d * mZoomManager.getScale()); - } - - /** - * Given an x coordinate in content space, convert it to view - * space. - */ - /*package*/ int contentToViewX(int x) { - return contentToViewDimension(x); - } - - /** - * Given a y coordinate in content space, convert it to view - * space. Takes into account the height of the title bar. - */ - /*package*/ int contentToViewY(int y) { - return contentToViewDimension(y) + getTitleHeight(); - } - - private Rect contentToViewRect(Rect x) { - return new Rect(contentToViewX(x.left), contentToViewY(x.top), - contentToViewX(x.right), contentToViewY(x.bottom)); - } - - /* To invalidate a rectangle in content coordinates, we need to transform - the rect into view coordinates, so we can then call invalidate(...). - - Normally, we would just call contentToView[XY](...), which eventually - calls Math.round(coordinate * mActualScale). However, for invalidates, - we need to account for the slop that occurs with antialiasing. To - address that, we are a little more liberal in the size of the rect that - we invalidate. - - This liberal calculation calls floor() for the top/left, and ceil() for - the bottom/right coordinates. This catches the possible extra pixels of - antialiasing that we might have missed with just round(). - */ - - // Called by JNI to invalidate the View, given rectangle coordinates in - // content space - private void viewInvalidate(int l, int t, int r, int b) { - final float scale = mZoomManager.getScale(); - final int dy = getTitleHeight(); - mWebView.invalidate((int)Math.floor(l * scale), - (int)Math.floor(t * scale) + dy, - (int)Math.ceil(r * scale), - (int)Math.ceil(b * scale) + dy); - } - - // Called by JNI to invalidate the View after a delay, given rectangle - // coordinates in content space - private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) { - final float scale = mZoomManager.getScale(); - final int dy = getTitleHeight(); - mWebView.postInvalidateDelayed(delay, - (int)Math.floor(l * scale), - (int)Math.floor(t * scale) + dy, - (int)Math.ceil(r * scale), - (int)Math.ceil(b * scale) + dy); - } - - private void invalidateContentRect(Rect r) { - viewInvalidate(r.left, r.top, r.right, r.bottom); - } - - // stop the scroll animation, and don't let a subsequent fling add - // to the existing velocity - private void abortAnimation() { - mScroller.abortAnimation(); - mLastVelocity = 0; - } - - /* call from webcoreview.draw(), so we're still executing in the UI thread - */ - private void recordNewContentSize(int w, int h, boolean updateLayout) { - - // premature data from webkit, ignore - if ((w | h) == 0) { - invalidate(); - return; - } - - // don't abort a scroll animation if we didn't change anything - if (mContentWidth != w || mContentHeight != h) { - // record new dimensions - mContentWidth = w; - mContentHeight = h; - // If history Picture is drawn, don't update scroll. They will be - // updated when we get out of that mode. - if (!mDrawHistory) { - // repin our scroll, taking into account the new content size - updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY())); - if (!mScroller.isFinished()) { - // We are in the middle of a scroll. Repin the final scroll - // position. - mScroller.setFinalX(pinLocX(mScroller.getFinalX())); - mScroller.setFinalY(pinLocY(mScroller.getFinalY())); - } - } - invalidate(); - } - contentSizeChanged(updateLayout); - } - - // Used to avoid sending many visible rect messages. - private Rect mLastVisibleRectSent = new Rect(); - private Rect mLastGlobalRect = new Rect(); - private Rect mVisibleRect = new Rect(); - private Rect mGlobalVisibleRect = new Rect(); - private Point mScrollOffset = new Point(); - - Rect sendOurVisibleRect() { - if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent; - calcOurContentVisibleRect(mVisibleRect); - // Rect.equals() checks for null input. - if (!mVisibleRect.equals(mLastVisibleRectSent)) { - if (!mBlockWebkitViewMessages) { - mScrollOffset.set(mVisibleRect.left, mVisibleRect.top); - mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET); - mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, - mSendScrollEvent ? 1 : 0, mScrollOffset); - } - mLastVisibleRectSent.set(mVisibleRect); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - } - if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect) - && !mGlobalVisibleRect.equals(mLastGlobalRect)) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + "," - + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b=" - + mGlobalVisibleRect.bottom); - } - // TODO: the global offset is only used by windowRect() - // in ChromeClientAndroid ; other clients such as touch - // and mouse events could return view + screen relative points. - if (!mBlockWebkitViewMessages) { - mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect); - } - mLastGlobalRect.set(mGlobalVisibleRect); - } - return mVisibleRect; - } - - private Point mGlobalVisibleOffset = new Point(); - // Sets r to be the visible rectangle of our webview in view coordinates - private void calcOurVisibleRect(Rect r) { - mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset); - r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y); - } - - // Sets r to be our visible rectangle in content coordinates - private void calcOurContentVisibleRect(Rect r) { - calcOurVisibleRect(r); - r.left = viewToContentX(r.left); - // viewToContentY will remove the total height of the title bar. Add - // the visible height back in to account for the fact that if the title - // bar is partially visible, the part of the visible rect which is - // displaying our content is displaced by that amount. - r.top = viewToContentY(r.top + getVisibleTitleHeightImpl()); - r.right = viewToContentX(r.right); - r.bottom = viewToContentY(r.bottom); - } - - private final Rect mTempContentVisibleRect = new Rect(); - // Sets r to be our visible rectangle in content coordinates. We use this - // method on the native side to compute the position of the fixed layers. - // Uses floating coordinates (necessary to correctly place elements when - // the scale factor is not 1) - private void calcOurContentVisibleRectF(RectF r) { - calcOurVisibleRect(mTempContentVisibleRect); - viewToContentVisibleRect(r, mTempContentVisibleRect); - } - - static class ViewSizeData { - int mWidth; - int mHeight; - float mHeightWidthRatio; - int mActualViewHeight; - int mTextWrapWidth; - int mAnchorX; - int mAnchorY; - float mScale; - boolean mIgnoreHeight; - } - - /** - * Compute unzoomed width and height, and if they differ from the last - * values we sent, send them to webkit (to be used as new viewport) - * - * @param force ensures that the message is sent to webkit even if the width - * or height has not changed since the last message - * - * @return true if new values were sent - */ - boolean sendViewSizeZoom(boolean force) { - if (mBlockWebkitViewMessages) return false; - if (mZoomManager.isPreventingWebkitUpdates()) return false; - - int viewWidth = getViewWidth(); - int newWidth = Math.round(viewWidth * mZoomManager.getInvScale()); - // This height could be fixed and be different from actual visible height. - int viewHeight = getViewHeightWithTitle() - getTitleHeight(); - int newHeight = Math.round(viewHeight * mZoomManager.getInvScale()); - // Make the ratio more accurate than (newHeight / newWidth), since the - // latter both are calculated and rounded. - float heightWidthRatio = (float) viewHeight / viewWidth; - /* - * Because the native side may have already done a layout before the - * View system was able to measure us, we have to send a height of 0 to - * remove excess whitespace when we grow our width. This will trigger a - * layout and a change in content size. This content size change will - * mean that contentSizeChanged will either call this method directly or - * indirectly from onSizeChanged. - */ - if (newWidth > mLastWidthSent && mWrapContent) { - newHeight = 0; - heightWidthRatio = 0; - } - // Actual visible content height. - int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale()); - // Avoid sending another message if the dimensions have not changed. - if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force || - actualViewHeight != mLastActualHeightSent) { - ViewSizeData data = new ViewSizeData(); - data.mWidth = newWidth; - data.mHeight = newHeight; - data.mHeightWidthRatio = heightWidthRatio; - data.mActualViewHeight = actualViewHeight; - data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale()); - data.mScale = mZoomManager.getScale(); - data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress() - && !mHeightCanMeasure; - data.mAnchorX = mZoomManager.getDocumentAnchorX(); - data.mAnchorY = mZoomManager.getDocumentAnchorY(); - mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data); - mLastWidthSent = newWidth; - mLastHeightSent = newHeight; - mLastActualHeightSent = actualViewHeight; - mZoomManager.clearDocumentAnchor(); - return true; - } - return false; - } - - /** - * Update the double-tap zoom. - */ - /* package */ void updateDoubleTapZoom(int doubleTapZoom) { - mZoomManager.updateDoubleTapZoom(doubleTapZoom); - } - - private int computeRealHorizontalScrollRange() { - if (mDrawHistory) { - return mHistoryWidth; - } else { - // to avoid rounding error caused unnecessary scrollbar, use floor - return (int) Math.floor(mContentWidth * mZoomManager.getScale()); - } - } - - @Override - public int computeHorizontalScrollRange() { - int range = computeRealHorizontalScrollRange(); - - // Adjust reported range if overscrolled to compress the scroll bars - final int scrollX = getScrollX(); - final int overscrollRight = computeMaxScrollX(); - if (scrollX < 0) { - range -= scrollX; - } else if (scrollX > overscrollRight) { - range += scrollX - overscrollRight; - } - - return range; - } - - @Override - public int computeHorizontalScrollOffset() { - return Math.max(getScrollX(), 0); - } - - private int computeRealVerticalScrollRange() { - if (mDrawHistory) { - return mHistoryHeight; - } else { - // to avoid rounding error caused unnecessary scrollbar, use floor - return (int) Math.floor(mContentHeight * mZoomManager.getScale()); - } - } - - @Override - public int computeVerticalScrollRange() { - int range = computeRealVerticalScrollRange(); - - // Adjust reported range if overscrolled to compress the scroll bars - final int scrollY = getScrollY(); - final int overscrollBottom = computeMaxScrollY(); - if (scrollY < 0) { - range -= scrollY; - } else if (scrollY > overscrollBottom) { - range += scrollY - overscrollBottom; - } - - return range; - } - - @Override - public int computeVerticalScrollOffset() { - return Math.max(getScrollY() - getTitleHeight(), 0); - } - - @Override - public int computeVerticalScrollExtent() { - return getViewHeight(); - } - - @Override - public void onDrawVerticalScrollBar(Canvas canvas, - Drawable scrollBar, - int l, int t, int r, int b) { - if (getScrollY() < 0) { - t -= getScrollY(); - } - scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b); - scrollBar.draw(canvas); - } - - @Override - public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY) { - // Special-case layer scrolling so that we do not trigger normal scroll - // updating. - if (mTouchMode == TOUCH_DRAG_TEXT_MODE) { - scrollEditText(scrollX, scrollY); - return; - } - if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { - scrollLayerTo(scrollX, scrollY); - animateHandles(); - return; - } - mInOverScrollMode = false; - int maxX = computeMaxScrollX(); - int maxY = computeMaxScrollY(); - if (maxX == 0) { - // do not over scroll x if the page just fits the screen - scrollX = pinLocX(scrollX); - } else if (scrollX < 0 || scrollX > maxX) { - mInOverScrollMode = true; - } - if (scrollY < 0 || scrollY > maxY) { - mInOverScrollMode = true; - } - - int oldX = getScrollX(); - int oldY = getScrollY(); - - mWebViewPrivate.super_scrollTo(scrollX, scrollY); - - animateHandles(); - - if (mOverScrollGlow != null) { - mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY); - } - } - - /** - * See {@link WebView#getUrl()} - */ - @Override - public String getUrl() { - WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem(); - return h != null ? h.getUrl() : null; - } - - /** - * See {@link WebView#getOriginalUrl()} - */ - @Override - public String getOriginalUrl() { - WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem(); - return h != null ? h.getOriginalUrl() : null; - } - - /** - * See {@link WebView#getTitle()} - */ - @Override - public String getTitle() { - WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem(); - return h != null ? h.getTitle() : null; - } - - /** - * See {@link WebView#getFavicon()} - */ - @Override - public Bitmap getFavicon() { - WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem(); - return h != null ? h.getFavicon() : null; - } - - /** - * See {@link WebView#getTouchIconUrl()} - */ - @Override - public String getTouchIconUrl() { - WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem(); - return h != null ? h.getTouchIconUrl() : null; - } - - /** - * See {@link WebView#getProgress()} - */ - @Override - public int getProgress() { - return mCallbackProxy.getProgress(); - } - - /** - * See {@link WebView#getContentHeight()} - */ - @Override - public int getContentHeight() { - return mContentHeight; - } - - /** - * See {@link WebView#getContentWidth()} - */ - @Override - public int getContentWidth() { - return mContentWidth; - } - - public int getPageBackgroundColor() { - if (mNativeClass == 0) return Color.WHITE; - return nativeGetBackgroundColor(mNativeClass); - } - - /** - * See {@link WebView#pauseTimers()} - */ - @Override - public void pauseTimers() { - mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS); - } - - /** - * See {@link WebView#resumeTimers()} - */ - @Override - public void resumeTimers() { - mWebViewCore.sendMessage(EventHub.RESUME_TIMERS); - } - - /** - * See {@link WebView#onPause()} - */ - @Override - public void onPause() { - if (!mIsPaused) { - mIsPaused = true; - mWebViewCore.sendMessage(EventHub.ON_PAUSE); - // We want to pause the current playing video when switching out - // from the current WebView/tab. - if (mHTML5VideoViewProxy != null) { - mHTML5VideoViewProxy.pauseAndDispatch(); - } - if (mNativeClass != 0) { - nativeSetPauseDrawing(mNativeClass, true); - } - - cancelDialogs(); - WebCoreThreadWatchdog.pause(); - } - } - - @Override - public void onWindowVisibilityChanged(int visibility) { - updateDrawingState(); - } - - void updateDrawingState() { - if (mNativeClass == 0 || mIsPaused) return; - if (mWebView.getWindowVisibility() != View.VISIBLE) { - nativeSetPauseDrawing(mNativeClass, true); - } else if (mWebView.getVisibility() != View.VISIBLE) { - nativeSetPauseDrawing(mNativeClass, true); - } else { - nativeSetPauseDrawing(mNativeClass, false); - } - } - - /** - * See {@link WebView#onResume()} - */ - @Override - public void onResume() { - if (mIsPaused) { - mIsPaused = false; - mWebViewCore.sendMessage(EventHub.ON_RESUME); - if (mNativeClass != 0) { - nativeSetPauseDrawing(mNativeClass, false); - } - } - // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need - // to ensure that the Watchdog thread is running for the new WebView, so call - // it outside the if block above. - WebCoreThreadWatchdog.resume(); - } - - /** - * See {@link WebView#isPaused()} - */ - @Override - public boolean isPaused() { - return mIsPaused; - } - - /** - * See {@link WebView#freeMemory()} - */ - @Override - public void freeMemory() { - mWebViewCore.sendMessage(EventHub.FREE_MEMORY); - } - - /** - * See {@link WebView#clearCache(boolean)} - */ - @Override - public void clearCache(boolean includeDiskFiles) { - // Note: this really needs to be a static method as it clears cache for all - // WebView. But we need mWebViewCore to send message to WebCore thread, so - // we can't make this static. - mWebViewCore.sendMessage(EventHub.CLEAR_CACHE, - includeDiskFiles ? 1 : 0, 0); - } - - /** - * See {@link WebView#clearFormData()} - */ - @Override - public void clearFormData() { - if (mAutoCompletePopup != null) { - mAutoCompletePopup.clearAdapter(); - } - } - - /** - * See {@link WebView#clearHistory()} - */ - @Override - public void clearHistory() { - mCallbackProxy.getBackForwardList().setClearPending(); - mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY); - } - - /** - * See {@link WebView#clearSslPreferences()} - */ - @Override - public void clearSslPreferences() { - mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE); - } - - /** - * See {@link WebView#copyBackForwardList()} - */ - @Override - public WebBackForwardListClassic copyBackForwardList() { - return mCallbackProxy.getBackForwardList().clone(); - } - - /** - * See {@link WebView#setFindListener(WebView.FindListener)}. - * @hide - */ - @Override - public void setFindListener(WebView.FindListener listener) { - mFindListener = listener; - } - - /** - * See {@link WebView#findNext(boolean)} - */ - @Override - public void findNext(boolean forward) { - if (0 == mNativeClass) return; // client isn't initialized - if (mFindRequest != null) { - mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0, mFindRequest); - } - } - - /** - * See {@link WebView#findAll(String)} - */ - @Override - public int findAll(String find) { - return findAllBody(find, false); - } - - @Override - public void findAllAsync(String find) { - findAllBody(find, true); - } - - private int findAllBody(String find, boolean isAsync) { - if (0 == mNativeClass) return 0; // client isn't initialized - mFindRequest = null; - if (find == null) return 0; - mWebViewCore.removeMessages(EventHub.FIND_ALL); - mFindRequest = new WebViewCore.FindAllRequest(find); - if (isAsync) { - mWebViewCore.sendMessage(EventHub.FIND_ALL, mFindRequest); - return 0; // no need to wait for response - } - synchronized(mFindRequest) { - try { - mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, mFindRequest); - while (mFindRequest.mMatchCount == -1) { - mFindRequest.wait(); - } - } - catch (InterruptedException e) { - return 0; - } - return mFindRequest.mMatchCount; - } - } - - /** - * Start an ActionMode for finding text in this WebView. Only works if this - * WebView is attached to the view system. - * @param text If non-null, will be the initial text to search for. - * Otherwise, the last String searched for in this WebView will - * be used to start. - * @param showIme If true, show the IME, assuming the user will begin typing. - * If false and text is non-null, perform a find all. - * @return boolean True if the find dialog is shown, false otherwise. - */ - @Override - public boolean showFindDialog(String text, boolean showIme) { - FindActionModeCallback callback = new FindActionModeCallback(mContext); - if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) { - // Could not start the action mode, so end Find on page - return false; - } - mCachedOverlappingActionModeHeight = -1; - mFindCallback = callback; - setFindIsUp(true); - mFindCallback.setWebView(getWebView()); - if (showIme) { - mFindCallback.showSoftInput(); - } else if (text != null) { - mFindCallback.setText(text); - mFindCallback.findAll(); - return true; - } - if (text == null) { - text = mFindRequest == null ? null : mFindRequest.mSearchText; - } - if (text != null) { - mFindCallback.setText(text); - mFindCallback.findAll(); - } - return true; - } - - /** - * Keep track of the find callback so that we can remove its titlebar if - * necessary. - */ - private FindActionModeCallback mFindCallback; - - /** - * Toggle whether the find dialog is showing, for both native and Java. - */ - private void setFindIsUp(boolean isUp) { - mFindIsUp = isUp; - } - - // Used to know whether the find dialog is open. Affects whether - // or not we draw the highlights for matches. - private boolean mFindIsUp; - - // Keep track of the last find request sent. - private WebViewCore.FindAllRequest mFindRequest = null; - - /** - * Return the first substring consisting of the address of a physical - * location. Currently, only addresses in the United States are detected, - * and consist of: - * - a house number - * - a street name - * - a street type (Road, Circle, etc), either spelled out or abbreviated - * - a city name - * - a state or territory, either spelled out or two-letter abbr. - * - an optional 5 digit or 9 digit zip code. - * - * All names must be correctly capitalized, and the zip code, if present, - * must be valid for the state. The street type must be a standard USPS - * spelling or abbreviation. The state or territory must also be spelled - * or abbreviated using USPS standards. The house number may not exceed - * five digits. - * @param addr The string to search for addresses. - * - * @return the address, or if no address is found, return null. - */ - public static String findAddress(String addr) { - return findAddress(addr, false); - } - - /** - * Return the first substring consisting of the address of a physical - * location. Currently, only addresses in the United States are detected, - * and consist of: - * - a house number - * - a street name - * - a street type (Road, Circle, etc), either spelled out or abbreviated - * - a city name - * - a state or territory, either spelled out or two-letter abbr. - * - an optional 5 digit or 9 digit zip code. - * - * Names are optionally capitalized, and the zip code, if present, - * must be valid for the state. The street type must be a standard USPS - * spelling or abbreviation. The state or territory must also be spelled - * or abbreviated using USPS standards. The house number may not exceed - * five digits. - * @param addr The string to search for addresses. - * @param caseInsensitive addr Set to true to make search ignore case. - * - * @return the address, or if no address is found, return null. - */ - public static String findAddress(String addr, boolean caseInsensitive) { - return WebViewCore.nativeFindAddress(addr, caseInsensitive); - } - - /** - * See {@link WebView#clearMatches()} - */ - @Override - public void clearMatches() { - if (mNativeClass == 0) - return; - mWebViewCore.removeMessages(EventHub.FIND_ALL); - mWebViewCore.sendMessage(EventHub.FIND_ALL, null); - } - - - /** - * Called when the find ActionMode ends. - */ - @Override - public void notifyFindDialogDismissed() { - mFindCallback = null; - mCachedOverlappingActionModeHeight = -1; - if (mWebViewCore == null) { - return; - } - clearMatches(); - setFindIsUp(false); - // Now that the dialog has been removed, ensure that we scroll to a - // location that is not beyond the end of the page. - pinScrollTo(getScrollX(), getScrollY(), false, 0); - invalidate(); - } - - /** - * See {@link WebView#documentHasImages(Message)} - */ - @Override - public void documentHasImages(Message response) { - if (response == null) { - return; - } - mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response); - } - - /** - * Request the scroller to abort any ongoing animation - */ - public void stopScroll() { - mScroller.forceFinished(true); - mLastVelocity = 0; - } - - @Override - public void computeScroll() { - if (mScroller.computeScrollOffset()) { - int oldX = getScrollX(); - int oldY = getScrollY(); - int x = mScroller.getCurrX(); - int y = mScroller.getCurrY(); - invalidate(); // So we draw again - - if (!mScroller.isFinished()) { - int rangeX = computeMaxScrollX(); - int rangeY = computeMaxScrollY(); - int overflingDistance = mOverflingDistance; - - // Use the layer's scroll data if needed. - if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { - oldX = mScrollingLayerRect.left; - oldY = mScrollingLayerRect.top; - rangeX = mScrollingLayerRect.right; - rangeY = mScrollingLayerRect.bottom; - // No overscrolling for layers. - overflingDistance = 0; - } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) { - oldX = getTextScrollX(); - oldY = getTextScrollY(); - rangeX = getMaxTextScrollX(); - rangeY = getMaxTextScrollY(); - overflingDistance = 0; - } - - mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY, - rangeX, rangeY, - overflingDistance, overflingDistance, false); - - if (mOverScrollGlow != null) { - mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY); - } - } else { - if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { - // Update the layer position instead of WebView. - scrollLayerTo(x, y); - } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) { - scrollEditText(x, y); - } else { - setScrollXRaw(x); - setScrollYRaw(y); - } - abortAnimation(); - nativeSetIsScrolling(false); - if (!mBlockWebkitViewMessages) { - WebViewCore.resumePriority(); - if (!mSelectingText) { - WebViewCore.resumeUpdatePicture(mWebViewCore); - } - } - if (oldX != getScrollX() || oldY != getScrollY()) { - sendOurVisibleRect(); - } - } - } else { - mWebViewPrivate.super_computeScroll(); - } - } - - private void scrollLayerTo(int x, int y) { - int dx = mScrollingLayerRect.left - x; - int dy = mScrollingLayerRect.top - y; - if ((dx == 0 && dy == 0) || mNativeClass == 0) { - return; - } - if (mSelectingText) { - if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) { - mSelectCursorBase.offset(dx, dy); - mSelectCursorBaseTextQuad.offset(dx, dy); - } - if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) { - mSelectCursorExtent.offset(dx, dy); - mSelectCursorExtentTextQuad.offset(dx, dy); - } - } - if (mAutoCompletePopup != null && - mCurrentScrollingLayerId == mEditTextLayerId) { - mEditTextContentBounds.offset(dx, dy); - mAutoCompletePopup.resetRect(); - } - nativeScrollLayer(mNativeClass, mCurrentScrollingLayerId, x, y); - mScrollingLayerRect.left = x; - mScrollingLayerRect.top = y; - mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId, - mScrollingLayerRect); - mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY()); - invalidate(); - } - - private static int computeDuration(int dx, int dy) { - int distance = Math.max(Math.abs(dx), Math.abs(dy)); - int duration = distance * 1000 / STD_SPEED; - return Math.min(duration, MAX_DURATION); - } - - // helper to pin the scrollBy parameters (already in view coordinates) - // returns true if the scroll was changed - private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) { - return pinScrollTo(getScrollX() + dx, getScrollY() + dy, animate, animationDuration); - } - // helper to pin the scrollTo parameters (already in view coordinates) - // returns true if the scroll was changed - private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) { - abortAnimation(); - x = pinLocX(x); - y = pinLocY(y); - int dx = x - getScrollX(); - int dy = y - getScrollY(); - - if ((dx | dy) == 0) { - return false; - } - if (animate) { - // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); - mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, - animationDuration > 0 ? animationDuration : computeDuration(dx, dy)); - invalidate(); - } else { - mWebView.scrollTo(x, y); - } - return true; - } - - // Scale from content to view coordinates, and pin. - // Also called by jni webview.cpp - private boolean setContentScrollBy(int cx, int cy, boolean animate) { - if (mDrawHistory) { - // disallow WebView to change the scroll position as History Picture - // is used in the view system. - // TODO: as we switchOutDrawHistory when trackball or navigation - // keys are hit, this should be safe. Right? - return false; - } - cx = contentToViewDimension(cx); - cy = contentToViewDimension(cy); - if (mHeightCanMeasure) { - // move our visible rect according to scroll request - if (cy != 0) { - Rect tempRect = new Rect(); - calcOurVisibleRect(tempRect); - tempRect.offset(cx, cy); - mWebView.requestRectangleOnScreen(tempRect); - } - // FIXME: We scroll horizontally no matter what because currently - // ScrollView and ListView will not scroll horizontally. - // FIXME: Why do we only scroll horizontally if there is no - // vertical scroll? -// Log.d(LOGTAG, "setContentScrollBy cy=" + cy); - return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0); - } else { - return pinScrollBy(cx, cy, animate, 0); - } - } - - /** - * Called by CallbackProxy when the page starts loading. - * @param url The URL of the page which has started loading. - */ - /* package */ void onPageStarted(String url) { - // every time we start a new page, we want to reset the - // WebView certificate: if the new site is secure, we - // will reload it and get a new certificate set; - // if the new site is not secure, the certificate must be - // null, and that will be the case - mWebView.setCertificate(null); - - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().onPageStarted(url); - } - - // Don't start out editing. - mIsEditingText = false; - } - - /** - * Called by CallbackProxy when the page finishes loading. - * @param url The URL of the page which has finished loading. - */ - /* package */ void onPageFinished(String url) { - mZoomManager.onPageFinished(url); - - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().onPageFinished(url); - } - } - - // scale from content to view coordinates, and pin - private void contentScrollTo(int cx, int cy, boolean animate) { - if (mDrawHistory) { - // disallow WebView to change the scroll position as History Picture - // is used in the view system. - return; - } - int vx = contentToViewX(cx); - int vy = contentToViewY(cy); - pinScrollTo(vx, vy, animate, 0); - } - - /** - * These are from webkit, and are in content coordinate system (unzoomed) - */ - private void contentSizeChanged(boolean updateLayout) { - // suppress 0,0 since we usually see real dimensions soon after - // this avoids drawing the prev content in a funny place. If we find a - // way to consolidate these notifications, this check may become - // obsolete - if ((mContentWidth | mContentHeight) == 0) { - return; - } - - if (mHeightCanMeasure) { - if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight) - || updateLayout) { - mWebView.requestLayout(); - } - } else if (mWidthCanMeasure) { - if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth) - || updateLayout) { - mWebView.requestLayout(); - } - } else { - // If we don't request a layout, try to send our view size to the - // native side to ensure that WebCore has the correct dimensions. - sendViewSizeZoom(false); - } - } - - /** - * See {@link WebView#setWebViewClient(WebViewClient)} - */ - @Override - public void setWebViewClient(WebViewClient client) { - mCallbackProxy.setWebViewClient(client); - } - - /** - * Gets the WebViewClient - * @return the current WebViewClient instance. - * - * This is an implementation detail. - */ - public WebViewClient getWebViewClient() { - return mCallbackProxy.getWebViewClient(); - } - - /** - * See {@link WebView#setDownloadListener(DownloadListener)} - */ - @Override - public void setDownloadListener(DownloadListener listener) { - mCallbackProxy.setDownloadListener(listener); - } - - /** - * See {@link WebView#setWebChromeClient(WebChromeClient)} - */ - @Override - public void setWebChromeClient(WebChromeClient client) { - mCallbackProxy.setWebChromeClient(client); - } - - /** - * Gets the chrome handler. - * @return the current WebChromeClient instance. - * - * This is an implementation detail. - */ - public WebChromeClient getWebChromeClient() { - return mCallbackProxy.getWebChromeClient(); - } - - /** - * Set the back/forward list client. This is an implementation of - * WebBackForwardListClient for handling new items and changes in the - * history index. - * @param client An implementation of WebBackForwardListClient. - */ - public void setWebBackForwardListClient(WebBackForwardListClient client) { - mCallbackProxy.setWebBackForwardListClient(client); - } - - /** - * Gets the WebBackForwardListClient. - */ - public WebBackForwardListClient getWebBackForwardListClient() { - return mCallbackProxy.getWebBackForwardListClient(); - } - - /** - * See {@link WebView#setPictureListener(PictureListener)} - */ - @Override - @Deprecated - public void setPictureListener(PictureListener listener) { - mPictureListener = listener; - } - - /* FIXME: Debug only! Remove for SDK! */ - public void externalRepresentation(Message callback) { - mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback); - } - - /* FIXME: Debug only! Remove for SDK! */ - public void documentAsText(Message callback) { - mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback); - } - - /** - * See {@link WebView#addJavascriptInterface(Object, String)} - */ - @Override - public void addJavascriptInterface(Object object, String name) { - - if (object == null) { - return; - } - WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); - - arg.mObject = object; - arg.mInterfaceName = name; - - // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to - // methods that are accessible from JS. - if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - arg.mRequireAnnotation = true; - } else { - arg.mRequireAnnotation = false; - } - mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); - } - - /** - * See {@link WebView#removeJavascriptInterface(String)} - */ - @Override - public void removeJavascriptInterface(String interfaceName) { - if (mWebViewCore != null) { - WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); - arg.mInterfaceName = interfaceName; - mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg); - } - } - - /** - * See {@link WebView#getSettings()} - * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used - * to access extension APIs. - */ - @Override - public WebSettingsClassic getSettings() { - return (mWebViewCore != null) ? mWebViewCore.getSettings() : null; - } - - /** - * See {@link WebView#getPluginList()} - */ - @Deprecated - public static synchronized PluginList getPluginList() { - return new PluginList(); - } - - /** - * See {@link WebView#refreshPlugins(boolean)} - */ - @Deprecated - public void refreshPlugins(boolean reloadOpenPages) { - } - - //------------------------------------------------------------------------- - // Override View methods - //------------------------------------------------------------------------- - - @Override - protected void finalize() throws Throwable { - try { - destroy(); - } finally { - super.finalize(); - } - } - - private void drawContent(Canvas canvas) { - if (mDrawHistory) { - canvas.scale(mZoomManager.getScale(), mZoomManager.getScale()); - canvas.drawPicture(mHistoryPicture); - return; - } - if (mNativeClass == 0) return; - - boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress(); - boolean animateScroll = ((!mScroller.isFinished() - || mVelocityTracker != null) - && (mTouchMode != TOUCH_DRAG_MODE || - mHeldMotionless != MOTIONLESS_TRUE)); - if (mTouchMode == TOUCH_DRAG_MODE) { - if (mHeldMotionless == MOTIONLESS_PENDING) { - mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS); - mHeldMotionless = MOTIONLESS_FALSE; - } - if (mHeldMotionless == MOTIONLESS_FALSE) { - mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME); - mHeldMotionless = MOTIONLESS_PENDING; - } - } - int saveCount = canvas.save(); - if (animateZoom) { - mZoomManager.animateZoom(canvas); - } else if (!canvas.isHardwareAccelerated()) { - canvas.scale(mZoomManager.getScale(), mZoomManager.getScale()); - } - - boolean UIAnimationsRunning = false; - // Currently for each draw we compute the animation values; - // We may in the future decide to do that independently. - if (mNativeClass != 0 && !canvas.isHardwareAccelerated() - && nativeEvaluateLayersAnimations(mNativeClass)) { - UIAnimationsRunning = true; - // If we have unfinished (or unstarted) animations, - // we ask for a repaint. We only need to do this in software - // rendering (with hardware rendering we already have a different - // method of requesting a repaint) - mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); - invalidate(); - } - - // decide which adornments to draw - int extras = DRAW_EXTRAS_NONE; - if (!mFindIsUp && mShowTextSelectionExtra) { - extras = DRAW_EXTRAS_SELECTION; - } - - calcOurContentVisibleRectF(mVisibleContentRect); - if (canvas.isHardwareAccelerated()) { - Rect invScreenRect = mIsWebViewVisible ? mInvScreenRect : null; - Rect screenRect = mIsWebViewVisible ? mScreenRect : null; - - int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect, - screenRect, mVisibleContentRect, getScale(), extras); - ((HardwareCanvas) canvas).callDrawGLFunction(functor); - if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) { - mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled(); - nativeUseHardwareAccelSkia(mHardwareAccelSkia); - } - - } else { - DrawFilter df = null; - if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) { - df = mZoomFilter; - } else if (animateScroll) { - df = mScrollFilter; - } - canvas.setDrawFilter(df); - nativeDraw(canvas, mVisibleContentRect, mBackgroundColor, extras); - canvas.setDrawFilter(null); - } - - canvas.restoreToCount(saveCount); - drawTextSelectionHandles(canvas); - - if (extras == DRAW_EXTRAS_CURSOR_RING) { - if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) { - mTouchMode = TOUCH_SHORTPRESS_MODE; - } - } - } - - /** - * Draw the background when beyond bounds - * @param canvas Canvas to draw into - */ - private void drawOverScrollBackground(Canvas canvas) { - if (mOverScrollBackground == null) { - mOverScrollBackground = new Paint(); - Bitmap bm = BitmapFactory.decodeResource( - mContext.getResources(), - com.android.internal.R.drawable.status_bar_background); - mOverScrollBackground.setShader(new BitmapShader(bm, - Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); - mOverScrollBorder = new Paint(); - mOverScrollBorder.setStyle(Paint.Style.STROKE); - mOverScrollBorder.setStrokeWidth(0); - mOverScrollBorder.setColor(0xffbbbbbb); - } - - int top = 0; - int right = computeRealHorizontalScrollRange(); - int bottom = top + computeRealVerticalScrollRange(); - // first draw the background and anchor to the top of the view - canvas.save(); - canvas.translate(getScrollX(), getScrollY()); - canvas.clipRect(-getScrollX(), top - getScrollY(), right - getScrollX(), bottom - - getScrollY(), Region.Op.DIFFERENCE); - canvas.drawPaint(mOverScrollBackground); - canvas.restore(); - // then draw the border - canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder); - // next clip the region for the content - canvas.clipRect(0, top, right, bottom); - } - - @Override - public void onDraw(Canvas canvas) { - if (inFullScreenMode()) { - return; // no need to draw anything if we aren't visible. - } - // if mNativeClass is 0, the WebView is either destroyed or not - // initialized. In either case, just draw the background color and return - if (mNativeClass == 0) { - canvas.drawColor(mBackgroundColor); - return; - } - - // if both mContentWidth and mContentHeight are 0, it means there is no - // valid Picture passed to WebView yet. This can happen when WebView - // just starts. Draw the background and return. - if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) { - canvas.drawColor(mBackgroundColor); - return; - } - - if (canvas.isHardwareAccelerated()) { - mZoomManager.setHardwareAccelerated(); - } else { - mWebViewCore.resumeWebKitDraw(); - } - - int saveCount = canvas.save(); - if (mInOverScrollMode && !getSettings() - .getUseWebViewBackgroundForOverscrollBackground()) { - drawOverScrollBackground(canvas); - } - - canvas.translate(0, getTitleHeight()); - drawContent(canvas); - canvas.restoreToCount(saveCount); - - if (AUTO_REDRAW_HACK && mAutoRedraw) { - invalidate(); - } - mWebViewCore.signalRepaintDone(); - - if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) { - invalidate(); - } - - if (mFocusTransition != null) { - mFocusTransition.draw(canvas); - } else if (shouldDrawHighlightRect()) { - RegionIterator iter = new RegionIterator(mTouchHighlightRegion); - Rect r = new Rect(); - while (iter.next(r)) { - canvas.drawRect(r, mTouchHightlightPaint); - } - } - if (DEBUG_TOUCH_HIGHLIGHT) { - if (getSettings().getNavDump()) { - if ((mTouchHighlightX | mTouchHighlightY) != 0) { - if (mTouchCrossHairColor == null) { - mTouchCrossHairColor = new Paint(); - mTouchCrossHairColor.setColor(Color.RED); - } - canvas.drawLine(mTouchHighlightX - mNavSlop, - mTouchHighlightY - mNavSlop, mTouchHighlightX - + mNavSlop + 1, mTouchHighlightY + mNavSlop - + 1, mTouchCrossHairColor); - canvas.drawLine(mTouchHighlightX + mNavSlop + 1, - mTouchHighlightY - mNavSlop, mTouchHighlightX - - mNavSlop, - mTouchHighlightY + mNavSlop + 1, - mTouchCrossHairColor); - } - } - } - } - - private void removeTouchHighlight() { - setTouchHighlightRects(null); - } - - @Override - public void setLayoutParams(ViewGroup.LayoutParams params) { - if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) { - mWrapContent = true; - } - mWebViewPrivate.super_setLayoutParams(params); - } - - @Override - public boolean performLongClick() { - // performLongClick() is the result of a delayed message. If we switch - // to windows overview, the WebView will be temporarily removed from the - // view system. In that case, do nothing. - if (mWebView.getParent() == null) return false; - - // A multi-finger gesture can look like a long press; make sure we don't take - // long press actions if we're scaling. - final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector(); - if (detector != null && detector.isInProgress()) { - return false; - } - - if (mSelectingText) return false; // long click does nothing on selection - /* if long click brings up a context menu, the super function - * returns true and we're done. Otherwise, nothing happened when - * the user clicked. */ - if (mWebViewPrivate.super_performLongClick()) { - return true; - } - /* In the case where the application hasn't already handled the long - * click action, look for a word under the click. If one is found, - * animate the text selection into view. - * FIXME: no animation code yet */ - final boolean isSelecting = selectText(); - if (isSelecting) { - mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } else if (focusCandidateIsEditableText()) { - mSelectCallback = new SelectActionModeCallback(); - mSelectCallback.setWebView(this); - mSelectCallback.setTextSelected(false); - mWebView.startActionMode(mSelectCallback); - } - return isSelecting; - } - - /** - * Select the word at the last click point. - * - * This is an implementation detail. - */ - public boolean selectText() { - int x = viewToContentX(mLastTouchX + getScrollX()); - int y = viewToContentY(mLastTouchY + getScrollY()); - return selectText(x, y); - } - - /** - * Select the word at the indicated content coordinates. - */ - boolean selectText(int x, int y) { - if (mWebViewCore == null) { - return false; - } - mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y); - return true; - } - - private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - - @Override - public void onConfigurationChanged(Configuration newConfig) { - mCachedOverlappingActionModeHeight = -1; - if (mSelectingText && mOrientation != newConfig.orientation) { - selectionDone(); - } - mOrientation = newConfig.orientation; - if (mWebViewCore != null && !mBlockWebkitViewMessages) { - mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT); - } - } - - /** - * Keep track of the Callback so we can end its ActionMode or remove its - * titlebar. - */ - private SelectActionModeCallback mSelectCallback; - - void setBaseLayer(int layer, boolean showVisualIndicator, - boolean isPictureAfterFirstLayout) { - if (mNativeClass == 0) - return; - boolean queueFull; - final int scrollingLayer = (mTouchMode == TOUCH_DRAG_LAYER_MODE) - ? mCurrentScrollingLayerId : 0; - queueFull = nativeSetBaseLayer(mNativeClass, layer, - showVisualIndicator, isPictureAfterFirstLayout, - scrollingLayer); - - if (queueFull) { - mWebViewCore.pauseWebKitDraw(); - } else { - mWebViewCore.resumeWebKitDraw(); - } - - if (mHTML5VideoViewProxy != null) { - mHTML5VideoViewProxy.setBaseLayer(layer); - } - } - - int getBaseLayer() { - if (mNativeClass == 0) { - return 0; - } - return nativeGetBaseLayer(mNativeClass); - } - - private void onZoomAnimationStart() { - } - - private void onZoomAnimationEnd() { - mPrivateHandler.sendEmptyMessage(RELOCATE_AUTO_COMPLETE_POPUP); - } - - void onFixedLengthZoomAnimationStart() { - WebViewCore.pauseUpdatePicture(getWebViewCore()); - onZoomAnimationStart(); - } - - void onFixedLengthZoomAnimationEnd() { - if (!mBlockWebkitViewMessages && !mSelectingText) { - WebViewCore.resumeUpdatePicture(mWebViewCore); - } - onZoomAnimationEnd(); - } - - private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | - Paint.DITHER_FLAG | - Paint.SUBPIXEL_TEXT_FLAG; - private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | - Paint.DITHER_FLAG; - - private final DrawFilter mZoomFilter = - new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); - // If we need to trade better quality for speed, set mScrollFilter to null - private final DrawFilter mScrollFilter = - new PaintFlagsDrawFilter(SCROLL_BITS, 0); - - private class SelectionHandleAlpha { - private int mAlpha = 0; - private int mTargetAlpha = 0; - - public void setAlpha(int alpha) { - mAlpha = alpha; - // TODO: Use partial invalidate - invalidate(); - } - - public int getAlpha() { - return mAlpha; - } - - public void setTargetAlpha(int alpha) { - mTargetAlpha = alpha; - } - - public int getTargetAlpha() { - return mTargetAlpha; - } - - } - - private void startSelectingText() { - mSelectingText = true; - mShowTextSelectionExtra = true; - animateHandles(); - } - - private void animateHandle(boolean canShow, ObjectAnimator animator, - Point selectionPoint, int selectionLayerId, - SelectionHandleAlpha alpha) { - boolean isVisible = canShow && mSelectingText - && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint) - || isHandleVisible(selectionPoint, selectionLayerId)); - int targetValue = isVisible ? 255 : 0; - if (targetValue != alpha.getTargetAlpha()) { - alpha.setTargetAlpha(targetValue); - animator.setIntValues(targetValue); - animator.setDuration(SELECTION_HANDLE_ANIMATION_MS); - animator.start(); - } - } - - private void animateHandles() { - boolean canShowBase = mSelectingText; - boolean canShowExtent = mSelectingText && !mIsCaretSelection; - animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase, - mSelectCursorBaseLayerId, mBaseAlpha); - animateHandle(canShowExtent, mExtentHandleAlphaAnimator, - mSelectCursorExtent, mSelectCursorExtentLayerId, - mExtentAlpha); - } - - private void endSelectingText() { - mSelectingText = false; - mShowTextSelectionExtra = false; - animateHandles(); - } - - private void ensureSelectionHandles() { - if (mSelectHandleCenter == null) { - mSelectHandleCenter = mContext.getResources().getDrawable( - com.android.internal.R.drawable.text_select_handle_middle).mutate(); - mSelectHandleLeft = mContext.getResources().getDrawable( - com.android.internal.R.drawable.text_select_handle_left).mutate(); - mSelectHandleRight = mContext.getResources().getDrawable( - com.android.internal.R.drawable.text_select_handle_right).mutate(); - // All handles have the same height, so we can save effort with - // this assumption. - mSelectOffset = new Point(0, - -mSelectHandleLeft.getIntrinsicHeight()); - } - } - - private void drawHandle(Point point, int handleId, Rect bounds, - int alpha, Canvas canvas) { - int offset; - int width; - int height; - Drawable drawable; - boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId); - if (isLeft) { - drawable = mSelectHandleLeft; - width = mSelectHandleLeft.getIntrinsicWidth(); - height = mSelectHandleLeft.getIntrinsicHeight(); - // Magic formula copied from TextView - offset = (width * 3) / 4; - } else { - drawable = mSelectHandleRight; - width = mSelectHandleRight.getIntrinsicWidth(); - height = mSelectHandleRight.getIntrinsicHeight(); - // Magic formula copied from TextView - offset = width / 4; - } - int x = contentToViewDimension(point.x); - int y = contentToViewDimension(point.y); - bounds.set(x - offset, y, x - offset + width, y + height); - drawable.setBounds(bounds); - drawable.setAlpha(alpha); - drawable.draw(canvas); - } - - private void drawTextSelectionHandles(Canvas canvas) { - if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) { - return; - } - ensureSelectionHandles(); - if (mIsCaretSelection) { - // Caret handle is centered - int x = contentToViewDimension(mSelectCursorBase.x) - - (mSelectHandleCenter.getIntrinsicWidth() / 2); - int y = contentToViewDimension(mSelectCursorBase.y); - mSelectHandleBaseBounds.set(x, y, - x + mSelectHandleCenter.getIntrinsicWidth(), - y + mSelectHandleCenter.getIntrinsicHeight()); - mSelectHandleCenter.setBounds(mSelectHandleBaseBounds); - mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha()); - mSelectHandleCenter.draw(canvas); - } else { - drawHandle(mSelectCursorBase, HANDLE_ID_BASE, - mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas); - drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT, - mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas); - } - } - - private boolean isHandleVisible(Point selectionPoint, int layerId) { - boolean isVisible = true; - if (mIsEditingText) { - isVisible = mEditTextContentBounds.contains(selectionPoint.x, - selectionPoint.y); - } - if (isVisible) { - isVisible = nativeIsPointVisible(mNativeClass, layerId, - selectionPoint.x, selectionPoint.y); - } - return isVisible; - } - - /** - * Takes an int[4] array as an output param with the values being - * startX, startY, endX, endY - */ - private void getSelectionHandles(int[] handles) { - handles[0] = mSelectCursorBase.x; - handles[1] = mSelectCursorBase.y; - handles[2] = mSelectCursorExtent.x; - handles[3] = mSelectCursorExtent.y; - } - - // draw history - private boolean mDrawHistory = false; - private Picture mHistoryPicture = null; - private int mHistoryWidth = 0; - private int mHistoryHeight = 0; - - // Only check the flag, can be called from WebCore thread - boolean drawHistory() { - return mDrawHistory; - } - - int getHistoryPictureWidth() { - return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0; - } - - // Should only be called in UI thread - void switchOutDrawHistory() { - if (null == mWebViewCore) return; // CallbackProxy may trigger this - if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) { - mDrawHistory = false; - mHistoryPicture = null; - invalidate(); - int oldScrollX = getScrollX(); - int oldScrollY = getScrollY(); - setScrollXRaw(pinLocX(getScrollX())); - setScrollYRaw(pinLocY(getScrollY())); - if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) { - mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY); - } else { - sendOurVisibleRect(); - } - } - } - - /** - * Delete text from start to end in the focused textfield. If there is no - * focus, or if start == end, silently fail. If start and end are out of - * order, swap them. - * @param start Beginning of selection to delete. - * @param end End of selection to delete. - */ - /* package */ void deleteSelection(int start, int end) { - mTextGeneration++; - WebViewCore.TextSelectionData data - = new WebViewCore.TextSelectionData(start, end, 0); - mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0, - data); - } - - /** - * Set the selection to (start, end) in the focused textfield. If start and - * end are out of order, swap them. - * @param start Beginning of selection. - * @param end End of selection. - */ - /* package */ void setSelection(int start, int end) { - if (mWebViewCore != null) { - mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end); - } - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - if (mInputConnection == null) { - mInputConnection = new WebViewInputConnection(); - mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection); - } - mInputConnection.setupEditorInfo(outAttrs); - return mInputConnection; - } - - private void relocateAutoCompletePopup() { - if (mAutoCompletePopup != null) { - mAutoCompletePopup.resetRect(); - mAutoCompletePopup.setText(mInputConnection.getEditable()); - } - } - - /** - * Called in response to a message from webkit telling us that the soft - * keyboard should be launched. - */ - private void displaySoftKeyboard(boolean isTextView) { - InputMethodManager imm = (InputMethodManager) - mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - - // bring it back to the default level scale so that user can enter text - boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale(); - if (zoom) { - mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY); - mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false); - } - // Used by plugins and contentEditable. - // Also used if the navigation cache is out of date, and - // does not recognize that a textfield is in focus. In that - // case, use WebView as the targeted view. - // see http://b/issue?id=2457459 - imm.showSoftInput(mWebView, 0); - } - - // Called by WebKit to instruct the UI to hide the keyboard - private void hideSoftKeyboard() { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && (imm.isActive(mWebView))) { - imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0); - } - } - - /** - * Called by AutoCompletePopup to find saved form data associated with the - * textfield - * @param name Name of the textfield. - * @param nodePointer Pointer to the node of the textfield, so it can be - * compared to the currently focused textfield when the data is - * retrieved. - * @param autoFillable true if WebKit has determined this field is part of - * a form that can be auto filled. - * @param autoComplete true if the attribute "autocomplete" is set to true - * on the textfield. - */ - /* package */ void requestFormData(String name, int nodePointer, - boolean autoFillable, boolean autoComplete) { - if (mWebViewCore.getSettings().getSaveFormData()) { - Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA); - update.arg1 = nodePointer; - RequestFormData updater = new RequestFormData(name, getUrl(), - update, autoFillable, autoComplete); - Thread t = new Thread(updater); - t.start(); - } - } - - /* - * This class requests an Adapter for the AutoCompletePopup which shows past - * entries stored in the database. It is a Runnable so that it can be done - * in its own thread, without slowing down the UI. - */ - private class RequestFormData implements Runnable { - private String mName; - private String mUrl; - private Message mUpdateMessage; - private boolean mAutoFillable; - private boolean mAutoComplete; - private WebSettingsClassic mWebSettings; - - public RequestFormData(String name, String url, Message msg, - boolean autoFillable, boolean autoComplete) { - mName = name; - mUrl = WebTextView.urlForAutoCompleteData(url); - mUpdateMessage = msg; - mAutoFillable = autoFillable; - mAutoComplete = autoComplete; - mWebSettings = getSettings(); - } - - @Override - public void run() { - ArrayList<String> pastEntries = new ArrayList<String>(); - - if (mAutoFillable) { - // Note that code inside the adapter click handler in AutoCompletePopup depends - // on the AutoFill item being at the top of the drop down list. If you change - // the order, make sure to do it there too! - if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) { - pastEntries.add(mWebView.getResources().getText( - com.android.internal.R.string.autofill_this_form).toString() + - " " + - mAutoFillData.getPreviewString()); - mAutoCompletePopup.setIsAutoFillProfileSet(true); - } else { - // There is no autofill profile set up yet, so add an option that - // will invite the user to set their profile up. - pastEntries.add(mWebView.getResources().getText( - com.android.internal.R.string.setup_autofill).toString()); - mAutoCompletePopup.setIsAutoFillProfileSet(false); - } - } - - if (mAutoComplete) { - pastEntries.addAll(mDatabase.getFormData(mUrl, mName)); - } - - if (pastEntries.size() > 0) { - ArrayAdapter<String> adapter = new ArrayAdapter<String>( - mContext, - com.android.internal.R.layout.web_text_view_dropdown, - pastEntries); - mUpdateMessage.obj = adapter; - mUpdateMessage.sendToTarget(); - } - } - } - - /** - * Dump the display tree to "/sdcard/displayTree.txt" - * - * debug only - */ - public void dumpDisplayTree() { - nativeDumpDisplayTree(getUrl()); - } - - /** - * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to - * "/sdcard/domTree.txt" - * - * debug only - */ - public void dumpDomTree(boolean toFile) { - mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0); - } - - /** - * Dump the render tree to adb shell if "toFile" is False, otherwise dump it - * to "/sdcard/renderTree.txt" - * - * debug only - */ - public void dumpRenderTree(boolean toFile) { - mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0); - } - - /** - * Called by DRT on UI thread, need to proxy to WebCore thread. - * - * debug only - */ - public void setUseMockDeviceOrientation() { - mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION); - } - - /** - * Sets use of the Geolocation mock client. Also resets that client. Called - * by DRT on UI thread, need to proxy to WebCore thread. - * - * debug only - */ - public void setUseMockGeolocation() { - mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION); - } - - /** - * Called by DRT on WebCore thread. - * - * debug only - */ - public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { - mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy); - } - - /** - * Called by DRT on WebCore thread. - * - * debug only - */ - public void setMockGeolocationError(int code, String message) { - mWebViewCore.setMockGeolocationError(code, message); - } - - /** - * Called by DRT on WebCore thread. - * - * debug only - */ - public void setMockGeolocationPermission(boolean allow) { - mWebViewCore.setMockGeolocationPermission(allow); - } - - /** - * Called by DRT on WebCore thread. - * - * debug only - */ - public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, - boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { - mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta, - canProvideGamma, gamma); - } - - // This is used to determine long press with the center key. Does not - // affect long press with the trackball/touch. - private boolean mGotCenterDown = false; - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { - if (mBlockWebkitViewMessages) { - return false; - } - // send complex characters to webkit for use by JS and plugins - if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) { - // pass the key to DOM - sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event); - sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event); - // return true as DOM handles the key - return true; - } - return false; - } - - private boolean isEnterActionKey(int keyCode) { - return keyCode == KeyEvent.KEYCODE_DPAD_CENTER - || keyCode == KeyEvent.KEYCODE_ENTER - || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER; - } - - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (mAutoCompletePopup != null) { - return mAutoCompletePopup.onKeyPreIme(keyCode, event); - } - return false; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis() - + "keyCode=" + keyCode - + ", " + event + ", unicode=" + event.getUnicodeChar()); - } - if (mIsCaretSelection) { - selectionDone(); - } - if (mBlockWebkitViewMessages) { - return false; - } - - // don't implement accelerator keys here; defer to host application - if (event.isCtrlPressed()) { - return false; - } - - if (mNativeClass == 0) { - return false; - } - - // do this hack up front, so it always works, regardless of touch-mode - if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) { - mAutoRedraw = !mAutoRedraw; - if (mAutoRedraw) { - invalidate(); - } - return true; - } - - // Bubble up the key event if - // 1. it is a system key; or - // 2. the host application wants to handle it; - if (event.isSystem() - || mCallbackProxy.uiOverrideKeyEvent(event)) { - return false; - } - - // See if the accessibility injector needs to handle this event. - if (isAccessibilityInjectionEnabled() - && getAccessibilityInjector().handleKeyEventIfNecessary(event)) { - return true; - } - - if (keyCode == KeyEvent.KEYCODE_PAGE_UP) { - if (event.hasNoModifiers()) { - pageUp(false); - return true; - } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) { - pageUp(true); - return true; - } - } - - if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) { - if (event.hasNoModifiers()) { - pageDown(false); - return true; - } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) { - pageDown(true); - return true; - } - } - - if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) { - pageUp(true); - return true; - } - - if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) { - pageDown(true); - return true; - } - - if (keyCode >= KeyEvent.KEYCODE_DPAD_UP - && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { - switchOutDrawHistory(); - } - - if (isEnterActionKey(keyCode)) { - switchOutDrawHistory(); - if (event.getRepeatCount() == 0) { - if (mSelectingText) { - return true; // discard press if copy in progress - } - mGotCenterDown = true; - mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT); - } - } - - if (getSettings().getNavDump()) { - switch (keyCode) { - case KeyEvent.KEYCODE_4: - dumpDisplayTree(); - break; - case KeyEvent.KEYCODE_5: - case KeyEvent.KEYCODE_6: - dumpDomTree(keyCode == KeyEvent.KEYCODE_5); - break; - case KeyEvent.KEYCODE_7: - case KeyEvent.KEYCODE_8: - dumpRenderTree(keyCode == KeyEvent.KEYCODE_7); - break; - } - } - - // pass the key to DOM - sendKeyEvent(event); - // return true as DOM handles the key - return true; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis() - + ", " + event + ", unicode=" + event.getUnicodeChar()); - } - if (mBlockWebkitViewMessages) { - return false; - } - - if (mNativeClass == 0) { - return false; - } - - // special CALL handling when cursor node's href is "tel:XXX" - if (keyCode == KeyEvent.KEYCODE_CALL - && mInitialHitTestResult != null - && mInitialHitTestResult.getType() == HitTestResult.PHONE_TYPE) { - String text = mInitialHitTestResult.getExtra(); - Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text)); - mContext.startActivity(intent); - return true; - } - - // Bubble up the key event if - // 1. it is a system key; or - // 2. the host application wants to handle it; - if (event.isSystem() - || mCallbackProxy.uiOverrideKeyEvent(event)) { - return false; - } - - // See if the accessibility injector needs to handle this event. - if (isAccessibilityInjectionEnabled() - && getAccessibilityInjector().handleKeyEventIfNecessary(event)) { - return true; - } - - if (isEnterActionKey(keyCode)) { - // remove the long press message first - mPrivateHandler.removeMessages(LONG_PRESS_CENTER); - mGotCenterDown = false; - - if (mSelectingText) { - copySelection(); - selectionDone(); - return true; // discard press if copy in progress - } - } - - // pass the key to DOM - sendKeyEvent(event); - // return true as DOM handles the key - return true; - } - - private boolean startSelectActionMode() { - mSelectCallback = new SelectActionModeCallback(); - mSelectCallback.setTextSelected(!mIsCaretSelection); - mSelectCallback.setWebView(this); - if (mWebView.startActionMode(mSelectCallback) == null) { - // There is no ActionMode, so do not allow the user to modify a - // selection. - selectionDone(); - return false; - } - mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - return true; - } - - private void showPasteWindow() { - ClipboardManager cm = (ClipboardManager)(mContext - .getSystemService(Context.CLIPBOARD_SERVICE)); - if (cm.hasPrimaryClip()) { - Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x), - contentToViewY(mSelectCursorBase.y)); - Point cursorTop = calculateBaseCaretTop(); - cursorTop.set(contentToViewX(cursorTop.x), - contentToViewY(cursorTop.y)); - - int[] location = new int[2]; - mWebView.getLocationInWindow(location); - int offsetX = location[0] - getScrollX(); - int offsetY = location[1] - getScrollY(); - cursorPoint.offset(offsetX, offsetY); - cursorTop.offset(offsetX, offsetY); - if (mPasteWindow == null) { - mPasteWindow = new PastePopupWindow(); - } - mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]); - } - } - - /** - * Given segment AB, this finds the point C along AB that is closest to - * point and then returns it scale along AB. The scale factor is AC/AB. - * - * @param x The x coordinate of the point near segment AB that determines - * the scale factor. - * @param y The y coordinate of the point near segment AB that determines - * the scale factor. - * @param a The first point of the line segment. - * @param b The second point of the line segment. - * @return The scale factor AC/AB, where C is the point on AB closest to - * point. - */ - private static float scaleAlongSegment(int x, int y, PointF a, PointF b) { - // The bottom line of the text box is line AB - float abX = b.x - a.x; - float abY = b.y - a.y; - float ab2 = (abX * abX) + (abY * abY); - - // The line from first point in text bounds to bottom is AP - float apX = x - a.x; - float apY = y - a.y; - float abDotAP = (apX * abX) + (apY * abY); - float scale = abDotAP / ab2; - return scale; - } - - private Point calculateBaseCaretTop() { - return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad); - } - - private Point calculateDraggingCaretTop() { - return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad); - } - - /** - * Assuming arbitrary shape of a quadralateral forming text bounds, this - * calculates the top of a caret. - */ - private static Point calculateCaretTop(Point base, QuadF quad) { - float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3); - int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x)); - int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y)); - return new Point(x, y); - } - - private void hidePasteButton() { - if (mPasteWindow != null) { - mPasteWindow.hide(); - } - } - - private void syncSelectionCursors() { - mSelectCursorBaseLayerId = - nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, - mSelectCursorBase, mSelectCursorBaseTextQuad); - mSelectCursorExtentLayerId = - nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, - mSelectCursorExtent, mSelectCursorExtentTextQuad); - } - - private boolean setupWebkitSelect() { - syncSelectionCursors(); - if (!mIsCaretSelection && !startSelectActionMode()) { - selectionDone(); - return false; - } - startSelectingText(); - mTouchMode = TOUCH_DRAG_MODE; - return true; - } - - private void updateWebkitSelection(boolean isSnapped) { - int handleId = (mSelectDraggingCursor == mSelectCursorBase) - ? HANDLE_ID_BASE : HANDLE_ID_EXTENT; - int x = mSelectDraggingCursor.x; - int y = mSelectDraggingCursor.y; - if (isSnapped) { - // "center" the cursor in the snapping quad - Point top = calculateDraggingCaretTop(); - x = Math.round((top.x + x) / 2); - y = Math.round((top.y + y) / 2); - } - mWebViewCore.removeMessages(EventHub.SELECT_TEXT); - mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, - x, y, (Integer)handleId); - } - - private void resetCaretTimer() { - mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); - if (!mSelectionStarted) { - mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE, - CARET_HANDLE_STAMINA_MS); - } - } - - /** - * Select all of the text in this WebView. - * - * This is an implementation detail. - */ - public void selectAll() { - mWebViewCore.sendMessage(EventHub.SELECT_ALL); - } - - /** - * Called when the selection has been removed. - */ - void selectionDone() { - if (mSelectingText) { - hidePasteButton(); - endSelectingText(); - // finish is idempotent, so this is fine even if selectionDone was - // called by mSelectCallback.onDestroyActionMode - if (mSelectCallback != null) { - mSelectCallback.finish(); - mSelectCallback = null; - } - invalidate(); // redraw without selection - mAutoScrollX = 0; - mAutoScrollY = 0; - mSentAutoScrollMessage = false; - } - } - - /** - * Copy the selection to the clipboard - * - * This is an implementation detail. - */ - public boolean copySelection() { - boolean copiedSomething = false; - String selection = getSelection(); - if (selection != null && selection != "") { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "copySelection \"" + selection + "\""); - } - Toast.makeText(mContext - , com.android.internal.R.string.text_copied - , Toast.LENGTH_SHORT).show(); - copiedSomething = true; - ClipboardManager cm = (ClipboardManager)mContext - .getSystemService(Context.CLIPBOARD_SERVICE); - cm.setText(selection); - int[] handles = new int[4]; - getSelectionHandles(handles); - mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles); - } - invalidate(); // remove selection region and pointer - return copiedSomething; - } - - /** - * Cut the selected text into the clipboard - * - * This is an implementation detail - */ - public void cutSelection() { - copySelection(); - int[] handles = new int[4]; - getSelectionHandles(handles); - mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles); - } - - /** - * Paste text from the clipboard to the cursor position. - * - * This is an implementation detail - */ - public void pasteFromClipboard() { - ClipboardManager cm = (ClipboardManager)mContext - .getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clipData = cm.getPrimaryClip(); - if (clipData != null) { - ClipData.Item clipItem = clipData.getItemAt(0); - CharSequence pasteText = clipItem.coerceToText(mContext); - if (mInputConnection != null) { - mInputConnection.replaceSelection(pasteText); - } - } - } - - /** - * Returns the currently highlighted text as a string. - */ - String getSelection() { - if (mNativeClass == 0) return ""; - return nativeGetSelection(); - } - - @Override - public void onAttachedToWindow() { - if (mWebView.hasWindowFocus()) setActive(true); - - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().toggleAccessibilityFeedback(true); - } - - updateHwAccelerated(); - } - - @Override - public void onDetachedFromWindow() { - clearHelpers(); - mZoomManager.dismissZoomPicker(); - if (mWebView.hasWindowFocus()) setActive(false); - - if (isAccessibilityInjectionEnabled()) { - getAccessibilityInjector().toggleAccessibilityFeedback(false); - } - - updateHwAccelerated(); - - ensureFunctorDetached(); - } - - @Override - public void onVisibilityChanged(View changedView, int visibility) { - // The zoomManager may be null if the webview is created from XML that - // specifies the view's visibility param as not visible (see http://b/2794841) - if (visibility != View.VISIBLE && mZoomManager != null) { - mZoomManager.dismissZoomPicker(); - } - updateDrawingState(); - } - - void setActive(boolean active) { - if (active) { - if (mWebView.hasFocus()) { - // If our window regained focus, and we have focus, then begin - // drawing the cursor ring - mDrawCursorRing = true; - setFocusControllerActive(true); - } else { - mDrawCursorRing = false; - setFocusControllerActive(false); - } - } else { - if (!mZoomManager.isZoomPickerVisible()) { - /* - * The external zoom controls come in their own window, so our - * window loses focus. Our policy is to not draw the cursor ring - * if our window is not focused, but this is an exception since - * the user can still navigate the web page with the zoom - * controls showing. - */ - mDrawCursorRing = false; - } - mKeysPressed.clear(); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mTouchMode = TOUCH_DONE_MODE; - setFocusControllerActive(false); - } - invalidate(); - } - - // To avoid drawing the cursor ring, and remove the TextView when our window - // loses focus. - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - setActive(hasWindowFocus); - if (hasWindowFocus) { - JWebCoreJavaBridge.setActiveWebView(this); - if (mPictureUpdatePausedForFocusChange) { - WebViewCore.resumeUpdatePicture(mWebViewCore); - mPictureUpdatePausedForFocusChange = false; - } - } else { - JWebCoreJavaBridge.removeActiveWebView(this); - final WebSettings settings = getSettings(); - if (settings != null && settings.enableSmoothTransition() && - mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) { - WebViewCore.pauseUpdatePicture(mWebViewCore); - mPictureUpdatePausedForFocusChange = true; - } - } - } - - /* - * Pass a message to WebCore Thread, telling the WebCore::Page's - * FocusController to be "inactive" so that it will - * not draw the blinking cursor. It gets set to "active" to draw the cursor - * in WebViewCore.cpp, when the WebCore thread receives key events/clicks. - */ - /* package */ void setFocusControllerActive(boolean active) { - if (mWebViewCore == null) return; - mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0); - // Need to send this message after the document regains focus. - if (active && mListBoxMessage != null) { - mWebViewCore.sendMessage(mListBoxMessage); - mListBoxMessage = null; - } - } - - @Override - public void onFocusChanged(boolean focused, int direction, - Rect previouslyFocusedRect) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction); - } - if (focused) { - mDrawCursorRing = true; - setFocusControllerActive(true); - } else { - mDrawCursorRing = false; - setFocusControllerActive(false); - mKeysPressed.clear(); - } - if (!mTouchHighlightRegion.isEmpty()) { - mWebView.invalidate(mTouchHighlightRegion.getBounds()); - } - } - - // updateRectsForGL() happens almost every draw call, in order to avoid creating - // any object in this code path, we move the local variable out to be a private - // final member, and we marked them as mTemp*. - private final Point mTempVisibleRectOffset = new Point(); - private final Rect mTempVisibleRect = new Rect(); - - void updateRectsForGL() { - // Use the getGlobalVisibleRect() to get the intersection among the parents - // visible == false means we're clipped - send a null rect down to indicate that - // we should not draw - boolean visible = mWebView.getGlobalVisibleRect(mTempVisibleRect, mTempVisibleRectOffset); - mInvScreenRect.set(mTempVisibleRect); - if (visible) { - // Then need to invert the Y axis, just for GL - View rootView = mWebView.getRootView(); - int rootViewHeight = rootView.getHeight(); - mScreenRect.set(mInvScreenRect); - int savedWebViewBottom = mInvScreenRect.bottom; - mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl(); - mInvScreenRect.top = rootViewHeight - savedWebViewBottom; - mIsWebViewVisible = true; - } else { - mIsWebViewVisible = false; - } - - mTempVisibleRect.offset(-mTempVisibleRectOffset.x, -mTempVisibleRectOffset.y); - viewToContentVisibleRect(mVisibleContentRect, mTempVisibleRect); - - nativeUpdateDrawGLFunction(mNativeClass, mIsWebViewVisible ? mInvScreenRect : null, - mIsWebViewVisible ? mScreenRect : null, - mVisibleContentRect, getScale()); - } - - // Input : viewRect, rect in view/screen coordinate. - // Output: contentRect, rect in content/document coordinate. - private void viewToContentVisibleRect(RectF contentRect, Rect viewRect) { - contentRect.left = viewToContentXf(viewRect.left) / mWebView.getScaleX(); - // viewToContentY will remove the total height of the title bar. Add - // the visible height back in to account for the fact that if the title - // bar is partially visible, the part of the visible rect which is - // displaying our content is displaced by that amount. - contentRect.top = viewToContentYf(viewRect.top + getVisibleTitleHeightImpl()) - / mWebView.getScaleY(); - contentRect.right = viewToContentXf(viewRect.right) / mWebView.getScaleX(); - contentRect.bottom = viewToContentYf(viewRect.bottom) / mWebView.getScaleY(); - } - - @Override - public boolean setFrame(int left, int top, int right, int bottom) { - boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom); - if (!changed && mHeightCanMeasure) { - // When mHeightCanMeasure is true, we will set mLastHeightSent to 0 - // in WebViewCore after we get the first layout. We do call - // requestLayout() when we get contentSizeChanged(). But the View - // system won't call onSizeChanged if the dimension is not changed. - // In this case, we need to call sendViewSizeZoom() explicitly to - // notify the WebKit about the new dimensions. - sendViewSizeZoom(false); - } - updateRectsForGL(); - return changed; - } - - @Override - public void onSizeChanged(int w, int h, int ow, int oh) { - // adjust the max viewport width depending on the view dimensions. This - // is to ensure the scaling is not going insane. So do not shrink it if - // the view size is temporarily smaller, e.g. when soft keyboard is up. - int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale()); - if (newMaxViewportWidth > sMaxViewportWidth) { - sMaxViewportWidth = newMaxViewportWidth; - } - - mZoomManager.onSizeChanged(w, h, ow, oh); - - if (mLoadedPicture != null && mDelaySetPicture == null) { - // Size changes normally result in a new picture - // Re-set the loaded picture to simulate that - // However, do not update the base layer as that hasn't changed - setNewPicture(mLoadedPicture, false); - } - if (mIsEditingText) { - scrollEditIntoView(); - } - relocateAutoCompletePopup(); - } - - /** - * Scrolls the edit field into view using the minimum scrolling necessary. - * If the edit field is too large to fit in the visible window, the caret - * dimensions are used so that at least the caret is visible. - * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the - * edit rectangle to ensure a margin with the edge of the screen. - */ - private void scrollEditIntoView() { - Rect visibleRect = new Rect(viewToContentX(getScrollX()), - viewToContentY(getScrollY()), - viewToContentX(getScrollX() + getWidth()), - viewToContentY(getScrollY() + getViewHeightWithTitle())); - if (visibleRect.contains(mEditTextContentBounds)) { - return; // no need to scroll - } - syncSelectionCursors(); - nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect); - final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER)); - Rect showRect = new Rect( - Math.max(0, mEditTextContentBounds.left - buffer), - Math.max(0, mEditTextContentBounds.top - buffer), - mEditTextContentBounds.right + buffer, - mEditTextContentBounds.bottom + buffer); - Point caretTop = calculateBaseCaretTop(); - if (visibleRect.width() < mEditTextContentBounds.width()) { - // The whole edit won't fit in the width, so use the caret rect - if (mSelectCursorBase.x < caretTop.x) { - showRect.left = Math.max(0, mSelectCursorBase.x - buffer); - showRect.right = caretTop.x + buffer; - } else { - showRect.left = Math.max(0, caretTop.x - buffer); - showRect.right = mSelectCursorBase.x + buffer; - } - } - if (visibleRect.height() < mEditTextContentBounds.height()) { - // The whole edit won't fit in the height, so use the caret rect - if (mSelectCursorBase.y > caretTop.y) { - showRect.top = Math.max(0, caretTop.y - buffer); - showRect.bottom = mSelectCursorBase.y + buffer; - } else { - showRect.top = Math.max(0, mSelectCursorBase.y - buffer); - showRect.bottom = caretTop.y + buffer; - } - } - - if (visibleRect.contains(showRect)) { - return; // no need to scroll - } - - int scrollX = viewToContentX(getScrollX()); - if (visibleRect.left > showRect.left) { - // We are scrolled too far - scrollX += showRect.left - visibleRect.left; - } else if (visibleRect.right < showRect.right) { - // We aren't scrolled enough to include the right - scrollX += showRect.right - visibleRect.right; - } - int scrollY = viewToContentY(getScrollY()); - if (visibleRect.top > showRect.top) { - scrollY += showRect.top - visibleRect.top; - } else if (visibleRect.bottom < showRect.bottom) { - scrollY += showRect.bottom - visibleRect.bottom; - } - - contentScrollTo(scrollX, scrollY, false); - } - - @Override - public void onScrollChanged(int l, int t, int oldl, int oldt) { - if (!mInOverScrollMode) { - sendOurVisibleRect(); - // update WebKit if visible title bar height changed. The logic is same - // as getVisibleTitleHeightImpl. - int titleHeight = getTitleHeight(); - if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) { - sendViewSizeZoom(false); - } - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - switch (event.getAction()) { - case KeyEvent.ACTION_DOWN: - mKeysPressed.add(Integer.valueOf(event.getKeyCode())); - break; - case KeyEvent.ACTION_MULTIPLE: - // Always accept the action. - break; - case KeyEvent.ACTION_UP: - int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode())); - if (location == -1) { - // We did not receive the key down for this key, so do not - // handle the key up. - return false; - } else { - // We did receive the key down. Handle the key up, and - // remove it from our pressed keys. - mKeysPressed.remove(location); - } - break; - default: - // Accept the action. This should not happen, unless a new - // action is added to KeyEvent. - break; - } - return mWebViewPrivate.super_dispatchKeyEvent(event); - } - - private static final int SNAP_BOUND = 16; - private static int sChannelDistance = 16; - private int mFirstTouchX = -1; // the first touched point - private int mFirstTouchY = -1; - private int mDistanceX = 0; - private int mDistanceY = 0; - - private boolean inFullScreenMode() { - return mFullScreenHolder != null; - } - - private void dismissFullScreenMode() { - if (inFullScreenMode()) { - mFullScreenHolder.hide(); - mFullScreenHolder = null; - invalidate(); - } - } - - void onPinchToZoomAnimationStart() { - // cancel the single touch handling - cancelTouch(); - onZoomAnimationStart(); - } - - void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) { - onZoomAnimationEnd(); - // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as - // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE - // as it may trigger the unwanted fling. - mTouchMode = TOUCH_PINCH_DRAG; - mConfirmMove = true; - startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime); - } - - // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a - // layer is found. - private void startScrollingLayer(float x, float y) { - if (mNativeClass == 0) - return; - - int contentX = viewToContentX((int) x + getScrollX()); - int contentY = viewToContentY((int) y + getScrollY()); - mCurrentScrollingLayerId = nativeScrollableLayer(mNativeClass, - contentX, contentY, mScrollingLayerRect, mScrollingLayerBounds); - if (mCurrentScrollingLayerId != 0) { - mTouchMode = TOUCH_DRAG_LAYER_MODE; - } - } - - // 1/(density * density) used to compute the distance between points. - // Computed in init(). - private float DRAG_LAYER_INVERSE_DENSITY_SQUARED; - - // The distance between two points reported in onTouchEvent scaled by the - // density of the screen. - private static final int DRAG_LAYER_FINGER_DISTANCE = 20000; - - @Override - public boolean onHoverEvent(MotionEvent event) { - if (mNativeClass == 0) { - return false; - } - int x = viewToContentX((int) event.getX() + getScrollX()); - int y = viewToContentY((int) event.getY() + getScrollY()); - mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, x, y); - mWebViewPrivate.super_onHoverEvent(event); - return true; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) { - return false; - } - - if (mInputDispatcher == null) { - return false; - } - - if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode() - && !mWebView.isFocused()) { - mWebView.requestFocus(); - } - - if (mInputDispatcher.postPointerEvent(ev, getScrollX(), - getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) { - mInputDispatcher.dispatchUiEvents(); - return true; - } else { - Log.w(LOGTAG, "mInputDispatcher rejected the event!"); - return false; - } - } - - /* - * Common code for single touch and multi-touch. - * (x, y) denotes current focus point, which is the touch point for single touch - * and the middle point for multi-touch. - */ - private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) { - ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector(); - - long eventTime = event.getEventTime(); - - // Due to the touch screen edge effect, a touch closer to the edge - // always snapped to the edge. As getViewWidth() can be different from - // getWidth() due to the scrollbar, adjusting the point to match - // getViewWidth(). Same applied to the height. - x = Math.min(x, getViewWidth() - 1); - y = Math.min(y, getViewHeightWithTitle() - 1); - - int deltaX = mLastTouchX - x; - int deltaY = mLastTouchY - y; - int contentX = viewToContentX(x + getScrollX()); - int contentY = viewToContentY(y + getScrollY()); - - switch (action) { - case MotionEvent.ACTION_DOWN: { - mConfirmMove = false; - - // Channel Scrolling - mFirstTouchX = x; - mFirstTouchY = y; - mDistanceX = mDistanceY = 0; - - if (!mEditTextScroller.isFinished()) { - mEditTextScroller.abortAnimation(); - } - if (!mScroller.isFinished()) { - // stop the current scroll animation, but if this is - // the start of a fling, allow it to add to the current - // fling's velocity - mScroller.abortAnimation(); - mTouchMode = TOUCH_DRAG_START_MODE; - mConfirmMove = true; - nativeSetIsScrolling(false); - } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) { - mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP); - removeTouchHighlight(); - if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) { - mTouchMode = TOUCH_DOUBLE_TAP_MODE; - } else { - mTouchMode = TOUCH_INIT_MODE; - } - } else { // the normal case - mTouchMode = TOUCH_INIT_MODE; - if (mLogEvent && eventTime - mLastTouchUpTime < 1000) { - EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION, - (eventTime - mLastTouchUpTime), eventTime); - } - mSelectionStarted = false; - if (mSelectingText) { - ensureSelectionHandles(); - int shiftedY = y - getTitleHeight() + getScrollY(); - int shiftedX = x + getScrollX(); - if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) { - mSelectionStarted = true; - mSelectDraggingCursor = mSelectCursorBase; - mSelectDraggingTextQuad = mSelectCursorBaseTextQuad; - if (mIsCaretSelection) { - mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); - hidePasteButton(); - } - } else if (mSelectHandleExtentBounds - .contains(shiftedX, shiftedY)) { - mSelectionStarted = true; - mSelectDraggingCursor = mSelectCursorExtent; - mSelectDraggingTextQuad = mSelectCursorExtentTextQuad; - } else if (mIsCaretSelection) { - selectionDone(); - } - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "select=" + contentX + "," + contentY); - } - } - } - // Trigger the link - if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE - || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) { - mPrivateHandler.sendEmptyMessageDelayed( - SWITCH_TO_SHORTPRESS, TAP_TIMEOUT); - mPrivateHandler.sendEmptyMessageDelayed( - SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT); - } - startTouch(x, y, eventTime); - if (mIsEditingText) { - mTouchInEditText = mEditTextContentBounds - .contains(contentX, contentY); - } - break; - } - case MotionEvent.ACTION_MOVE: { - if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY) - >= mTouchSlopSquare) { - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mConfirmMove = true; - if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) { - mTouchMode = TOUCH_INIT_MODE; - } - removeTouchHighlight(); - } - if (mSelectingText && mSelectionStarted) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "extend=" + contentX + "," + contentY); - } - ViewParent parent = mWebView.getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - if (deltaX != 0 || deltaY != 0) { - int handleX = contentX + - viewToContentDimension(mSelectOffset.x); - int handleY = contentY + - viewToContentDimension(mSelectOffset.y); - mSelectDraggingCursor.set(handleX, handleY); - boolean inCursorText = - mSelectDraggingTextQuad.containsPoint(handleX, handleY); - boolean inEditBounds = mEditTextContentBounds - .contains(handleX, handleY); - if (mIsEditingText && !inEditBounds) { - beginScrollEdit(); - } else { - endScrollEdit(); - } - boolean snapped = false; - if (inCursorText || (mIsEditingText && !inEditBounds)) { - snapDraggingCursor(); - snapped = true; - } - updateWebkitSelection(snapped); - if (!inCursorText && mIsEditingText && inEditBounds) { - // Visually snap even if we have moved the handle. - snapDraggingCursor(); - } - mLastTouchX = x; - mLastTouchY = y; - invalidate(); - } - break; - } - - if (mTouchMode == TOUCH_DONE_MODE) { - // no dragging during scroll zoom animation, or when prevent - // default is yes - break; - } - if (mVelocityTracker == null) { - Log.e(LOGTAG, "Got null mVelocityTracker when " - + " mTouchMode = " + mTouchMode); - } else { - mVelocityTracker.addMovement(event); - } - - if (mTouchMode != TOUCH_DRAG_MODE && - mTouchMode != TOUCH_DRAG_LAYER_MODE && - mTouchMode != TOUCH_DRAG_TEXT_MODE) { - - if (!mConfirmMove) { - break; - } - - if ((detector == null || !detector.isInProgress()) - && SNAP_NONE == mSnapScrollMode) { - int ax = Math.abs(x - mFirstTouchX); - int ay = Math.abs(y - mFirstTouchY); - if (ax < SNAP_BOUND && ay < SNAP_BOUND) { - break; - } else if (ax < SNAP_BOUND) { - mSnapScrollMode = SNAP_Y; - } else if (ay < SNAP_BOUND) { - mSnapScrollMode = SNAP_X; - } - } - - mTouchMode = TOUCH_DRAG_MODE; - mLastTouchX = x; - mLastTouchY = y; - deltaX = 0; - deltaY = 0; - - startScrollingLayer(x, y); - startDrag(); - } - - // do pan - boolean keepScrollBarsVisible = false; - if (deltaX == 0 && deltaY == 0) { - keepScrollBarsVisible = true; - } else { - if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) { - mDistanceX += Math.abs(deltaX); - mDistanceY += Math.abs(deltaY); - if (mSnapScrollMode == SNAP_X) { - if (mDistanceY > sChannelDistance) { - mSnapScrollMode = SNAP_NONE; - } else if (mDistanceX > sChannelDistance) { - mDistanceX = mDistanceY = 0; - } - } else { - if (mDistanceX > sChannelDistance) { - mSnapScrollMode = SNAP_NONE; - } else if (mDistanceY > sChannelDistance) { - mDistanceX = mDistanceY = 0; - } - } - } - if (mSnapScrollMode != SNAP_NONE) { - if ((mSnapScrollMode & SNAP_X) == SNAP_X) { - deltaY = 0; - } else { - deltaX = 0; - } - } - if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) { - mHeldMotionless = MOTIONLESS_FALSE; - } else { - mHeldMotionless = MOTIONLESS_TRUE; - keepScrollBarsVisible = true; - } - - mLastTouchTime = eventTime; - boolean allDrag = doDrag(deltaX, deltaY); - if (allDrag) { - mLastTouchX = x; - mLastTouchY = y; - } else { - int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale()); - int roundedDeltaX = contentToViewDimension(contentDeltaX); - int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale()); - int roundedDeltaY = contentToViewDimension(contentDeltaY); - mLastTouchX -= roundedDeltaX; - mLastTouchY -= roundedDeltaY; - } - } - - break; - } - case MotionEvent.ACTION_UP: { - mFirstTouchX = mFirstTouchY = -1; - if (mIsEditingText && mSelectionStarted) { - endScrollEdit(); - mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW, - TEXT_SCROLL_FIRST_SCROLL_MS); - if (!mConfirmMove && mIsCaretSelection) { - showPasteWindow(); - stopTouch(); - break; - } - } - mLastTouchUpTime = eventTime; - if (mSentAutoScrollMessage) { - mAutoScrollX = mAutoScrollY = 0; - } - switch (mTouchMode) { - case TOUCH_DOUBLE_TAP_MODE: // double tap - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mTouchMode = TOUCH_DONE_MODE; - break; - case TOUCH_INIT_MODE: // tap - case TOUCH_SHORTPRESS_START_MODE: - case TOUCH_SHORTPRESS_MODE: - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - if (!mConfirmMove) { - if (mSelectingText) { - // tapping on selection or controls does nothing - if (!mSelectionStarted) { - selectionDone(); - } - break; - } - // only trigger double tap if the WebView is - // scalable - if (mTouchMode == TOUCH_INIT_MODE - && (canZoomIn() || canZoomOut())) { - mPrivateHandler.sendEmptyMessageDelayed( - RELEASE_SINGLE_TAP, ViewConfiguration - .getDoubleTapTimeout()); - } - break; - } - case TOUCH_DRAG_MODE: - case TOUCH_DRAG_LAYER_MODE: - case TOUCH_DRAG_TEXT_MODE: - mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS); - // if the user waits a while w/o moving before the - // up, we don't want to do a fling - if (eventTime - mLastTouchTime <= MIN_FLING_TIME) { - if (mVelocityTracker == null) { - Log.e(LOGTAG, "Got null mVelocityTracker"); - } else { - mVelocityTracker.addMovement(event); - } - // set to MOTIONLESS_IGNORE so that it won't keep - // removing and sending message in - // drawCoreAndCursorRing() - mHeldMotionless = MOTIONLESS_IGNORE; - doFling(); - break; - } else { - if (mScroller.springBack(getScrollX(), getScrollY(), 0, - computeMaxScrollX(), 0, - computeMaxScrollY())) { - invalidate(); - } - } - // redraw in high-quality, as we're done dragging - mHeldMotionless = MOTIONLESS_TRUE; - invalidate(); - // fall through - case TOUCH_DRAG_START_MODE: - // TOUCH_DRAG_START_MODE should not happen for the real - // device as we almost certain will get a MOVE. But this - // is possible on emulator. - mLastVelocity = 0; - WebViewCore.resumePriority(); - if (!mSelectingText) { - WebViewCore.resumeUpdatePicture(mWebViewCore); - } - break; - } - stopTouch(); - break; - } - case MotionEvent.ACTION_CANCEL: { - if (mTouchMode == TOUCH_DRAG_MODE) { - mScroller.springBack(getScrollX(), getScrollY(), 0, - computeMaxScrollX(), 0, computeMaxScrollY()); - invalidate(); - } - cancelTouch(); - break; - } - } - } - - /** - * Returns the text scroll speed in content pixels per millisecond based on - * the touch location. - * @param coordinate The x or y touch coordinate in content space - * @param min The minimum coordinate (x or y) of the edit content bounds - * @param max The maximum coordinate (x or y) of the edit content bounds - */ - private static float getTextScrollSpeed(int coordinate, int min, int max) { - if (coordinate < min) { - return (coordinate - min) * TEXT_SCROLL_RATE; - } else if (coordinate >= max) { - return (coordinate - max + 1) * TEXT_SCROLL_RATE; - } else { - return 0.0f; - } - } - - private static int getSelectionCoordinate(int coordinate, int min, int max) { - return Math.max(Math.min(coordinate, max), min); - } - - private void beginScrollEdit() { - if (mLastEditScroll == 0) { - mLastEditScroll = SystemClock.uptimeMillis() - - TEXT_SCROLL_FIRST_SCROLL_MS; - scrollEditWithCursor(); - } - } - - private void scrollDraggedSelectionHandleIntoView() { - if (mSelectDraggingCursor == null) { - return; - } - int x = mSelectDraggingCursor.x; - int y = mSelectDraggingCursor.y; - if (!mEditTextContentBounds.contains(x,y)) { - int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER); - int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER); - int deltaX = left + right; - int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER); - int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER); - int deltaY = above + below; - if (deltaX != 0 || deltaY != 0) { - int scrollX = getTextScrollX() + deltaX; - int scrollY = getTextScrollY() + deltaY; - scrollX = clampBetween(scrollX, 0, getMaxTextScrollX()); - scrollY = clampBetween(scrollY, 0, getMaxTextScrollY()); - scrollEditText(scrollX, scrollY); - } - } - } - - private void endScrollEdit() { - mLastEditScroll = 0; - } - - private static int clampBetween(int value, int min, int max) { - return Math.max(min, Math.min(value, max)); - } - - private static int getTextScrollDelta(float speed, long deltaT) { - float distance = speed * deltaT; - int intDistance = (int)Math.floor(distance); - float probability = distance - intDistance; - if (Math.random() < probability) { - intDistance++; - } - return intDistance; - } - /** - * Scrolls edit text a distance based on the last touch point, - * the last scroll time, and the edit text content bounds. - */ - private void scrollEditWithCursor() { - if (mLastEditScroll != 0) { - int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x); - float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left, - mEditTextContentBounds.right); - int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y); - float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top, - mEditTextContentBounds.bottom); - if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) { - endScrollEdit(); - } else { - long currentTime = SystemClock.uptimeMillis(); - long timeSinceLastUpdate = currentTime - mLastEditScroll; - int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate); - int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate); - int scrollX = getTextScrollX() + deltaX; - scrollX = clampBetween(scrollX, 0, getMaxTextScrollX()); - int scrollY = getTextScrollY() + deltaY; - scrollY = clampBetween(scrollY, 0, getMaxTextScrollY()); - - mLastEditScroll = currentTime; - if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) { - // By probability no text scroll this time. Try again later. - mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT, - TEXT_SCROLL_FIRST_SCROLL_MS); - } else { - int selectionX = getSelectionCoordinate(x, - mEditTextContentBounds.left, mEditTextContentBounds.right); - int selectionY = getSelectionCoordinate(y, - mEditTextContentBounds.top, mEditTextContentBounds.bottom); - int oldX = mSelectDraggingCursor.x; - int oldY = mSelectDraggingCursor.y; - mSelectDraggingCursor.set(selectionX, selectionY); - updateWebkitSelection(false); - scrollEditText(scrollX, scrollY); - mSelectDraggingCursor.set(oldX, oldY); - } - } - } - } - - private void startTouch(float x, float y, long eventTime) { - // Remember where the motion event started - mStartTouchX = mLastTouchX = Math.round(x); - mStartTouchY = mLastTouchY = Math.round(y); - mLastTouchTime = eventTime; - mVelocityTracker = VelocityTracker.obtain(); - mSnapScrollMode = SNAP_NONE; - } - - private void startDrag() { - WebViewCore.reducePriority(); - // to get better performance, pause updating the picture - WebViewCore.pauseUpdatePicture(mWebViewCore); - nativeSetIsScrolling(true); - - if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF - || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) { - mZoomManager.invokeZoomPicker(); - } - } - - private boolean doDrag(int deltaX, int deltaY) { - boolean allDrag = true; - if ((deltaX | deltaY) != 0) { - int oldX = getScrollX(); - int oldY = getScrollY(); - int rangeX = computeMaxScrollX(); - int rangeY = computeMaxScrollY(); - final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale()); - final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale()); - - // Assume page scrolling and change below if we're wrong - mTouchMode = TOUCH_DRAG_MODE; - - // Check special scrolling before going to main page scrolling. - if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) { - // Edit text scrolling - oldX = getTextScrollX(); - rangeX = getMaxTextScrollX(); - deltaX = contentX; - oldY = getTextScrollY(); - rangeY = getMaxTextScrollY(); - deltaY = contentY; - mTouchMode = TOUCH_DRAG_TEXT_MODE; - allDrag = false; - } else if (mCurrentScrollingLayerId != 0) { - // Check the scrolling bounds to see if we will actually do any - // scrolling. The rectangle is in document coordinates. - final int maxX = mScrollingLayerRect.right; - final int maxY = mScrollingLayerRect.bottom; - final int resultX = clampBetween(maxX, 0, - mScrollingLayerRect.left + contentX); - final int resultY = clampBetween(maxY, 0, - mScrollingLayerRect.top + contentY); - - if (resultX != mScrollingLayerRect.left - || resultY != mScrollingLayerRect.top - || (contentX | contentY) == 0) { - // In case we switched to dragging the page. - mTouchMode = TOUCH_DRAG_LAYER_MODE; - deltaX = contentX; - deltaY = contentY; - oldX = mScrollingLayerRect.left; - oldY = mScrollingLayerRect.top; - rangeX = maxX; - rangeY = maxY; - allDrag = false; - } - } - - if (mOverScrollGlow != null) { - mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); - } - - mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY, - rangeX, rangeY, - mOverscrollDistance, mOverscrollDistance, true); - if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { - invalidate(); - } - } - mZoomManager.keepZoomPickerVisible(); - return allDrag; - } - - private void stopTouch() { - if (mScroller.isFinished() && !mSelectingText - && (mTouchMode == TOUCH_DRAG_MODE - || mTouchMode == TOUCH_DRAG_LAYER_MODE)) { - WebViewCore.resumePriority(); - WebViewCore.resumeUpdatePicture(mWebViewCore); - nativeSetIsScrolling(false); - } - - // we also use mVelocityTracker == null to tell us that we are - // not "moving around", so we can take the slower/prettier - // mode in the drawing code - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - // Release any pulled glows - if (mOverScrollGlow != null) { - mOverScrollGlow.releaseAll(); - } - - if (mSelectingText) { - mSelectionStarted = false; - syncSelectionCursors(); - if (mIsCaretSelection) { - resetCaretTimer(); - } - invalidate(); - } - } - - private void cancelTouch() { - // we also use mVelocityTracker == null to tell us that we are - // not "moving around", so we can take the slower/prettier - // mode in the drawing code - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - if ((mTouchMode == TOUCH_DRAG_MODE - || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) { - WebViewCore.resumePriority(); - WebViewCore.resumeUpdatePicture(mWebViewCore); - nativeSetIsScrolling(false); - } - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS); - removeTouchHighlight(); - mHeldMotionless = MOTIONLESS_TRUE; - mTouchMode = TOUCH_DONE_MODE; - } - - private void snapDraggingCursor() { - float scale = scaleAlongSegment( - mSelectDraggingCursor.x, mSelectDraggingCursor.y, - mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3); - // clamp scale to ensure point is on the bottom segment - scale = Math.max(0.0f, scale); - scale = Math.min(scale, 1.0f); - float newX = scaleCoordinate(scale, - mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x); - float newY = scaleCoordinate(scale, - mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y); - int x = Math.round(newX); - int y = Math.round(newY); - if (mIsEditingText) { - x = clampBetween(x, mEditTextContentBounds.left, - mEditTextContentBounds.right); - y = clampBetween(y, mEditTextContentBounds.top, - mEditTextContentBounds.bottom); - } - mSelectDraggingCursor.set(x, y); - } - - private static float scaleCoordinate(float scale, float coord1, float coord2) { - float diff = coord2 - coord1; - return coord1 + (scale * diff); - } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - switch (event.getAction()) { - case MotionEvent.ACTION_SCROLL: { - final float vscroll; - final float hscroll; - if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { - vscroll = 0; - hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - } else { - vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); - hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); - } - if (hscroll != 0 || vscroll != 0) { - final int vdelta = (int) (vscroll * - mWebViewPrivate.getVerticalScrollFactor()); - final int hdelta = (int) (hscroll * - mWebViewPrivate.getHorizontalScrollFactor()); - - abortAnimation(); - int oldTouchMode = mTouchMode; - startScrollingLayer(event.getX(), event.getY()); - doDrag(hdelta, vdelta); - mTouchMode = oldTouchMode; - return true; - } - } - } - } - return mWebViewPrivate.super_onGenericMotionEvent(event); - } - - private long mTrackballFirstTime = 0; - private long mTrackballLastTime = 0; - private float mTrackballRemainsX = 0.0f; - private float mTrackballRemainsY = 0.0f; - private int mTrackballXMove = 0; - private int mTrackballYMove = 0; - private boolean mSelectingText = false; - private boolean mShowTextSelectionExtra = false; - private boolean mSelectionStarted = false; - private static final int TRACKBALL_KEY_TIMEOUT = 1000; - private static final int TRACKBALL_TIMEOUT = 200; - private static final int TRACKBALL_WAIT = 100; - private static final int TRACKBALL_SCALE = 400; - private static final int TRACKBALL_SCROLL_COUNT = 5; - private static final int TRACKBALL_MOVE_COUNT = 10; - private static final int TRACKBALL_MULTIPLIER = 3; - private static final int SELECT_CURSOR_OFFSET = 16; - private static final int SELECT_SCROLL = 5; - private int mSelectX = 0; - private int mSelectY = 0; - private boolean mTrackballDown = false; - private long mTrackballUpTime = 0; - private long mLastCursorTime = 0; - private Rect mLastCursorBounds; - private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha(); - private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha(); - private ObjectAnimator mBaseHandleAlphaAnimator = - ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0); - private ObjectAnimator mExtentHandleAlphaAnimator = - ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0); - - // Set by default; BrowserActivity clears to interpret trackball data - // directly for movement. Currently, the framework only passes - // arrow key events, not trackball events, from one child to the next - private boolean mMapTrackballToArrowKeys = true; - - private DrawData mDelaySetPicture; - private DrawData mLoadedPicture; - - @Override - public void setMapTrackballToArrowKeys(boolean setMap) { - mMapTrackballToArrowKeys = setMap; - } - - void resetTrackballTime() { - mTrackballLastTime = 0; - } - - @Override - public boolean onTrackballEvent(MotionEvent ev) { - long time = ev.getEventTime(); - if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) { - if (ev.getY() > 0) pageDown(true); - if (ev.getY() < 0) pageUp(true); - return true; - } - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (mSelectingText) { - return true; // discard press if copy in progress - } - mTrackballDown = true; - if (mNativeClass == 0) { - return false; - } - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "onTrackballEvent down ev=" + ev - + " time=" + time - + " mLastCursorTime=" + mLastCursorTime); - } - if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch(); - return false; // let common code in onKeyDown at it - } - if (ev.getAction() == MotionEvent.ACTION_UP) { - // LONG_PRESS_CENTER is set in common onKeyDown - mPrivateHandler.removeMessages(LONG_PRESS_CENTER); - mTrackballDown = false; - mTrackballUpTime = time; - if (mSelectingText) { - copySelection(); - selectionDone(); - return true; // discard press if copy in progress - } - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "onTrackballEvent up ev=" + ev - + " time=" + time - ); - } - return false; // let common code in onKeyUp at it - } - if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) || - AccessibilityManager.getInstance(mContext).isEnabled()) { - if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit"); - return false; - } - if (mTrackballDown) { - if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit"); - return true; // discard move if trackball is down - } - if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) { - if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit"); - return true; - } - // TODO: alternatively we can do panning as touch does - switchOutDrawHistory(); - if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "onTrackballEvent time=" - + time + " last=" + mTrackballLastTime); - } - mTrackballFirstTime = time; - mTrackballXMove = mTrackballYMove = 0; - } - mTrackballLastTime = time; - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time); - } - mTrackballRemainsX += ev.getX(); - mTrackballRemainsY += ev.getY(); - doTrackball(time, ev.getMetaState()); - return true; - } - - private int scaleTrackballX(float xRate, int width) { - int xMove = (int) (xRate / TRACKBALL_SCALE * width); - int nextXMove = xMove; - if (xMove > 0) { - if (xMove > mTrackballXMove) { - xMove -= mTrackballXMove; - } - } else if (xMove < mTrackballXMove) { - xMove -= mTrackballXMove; - } - mTrackballXMove = nextXMove; - return xMove; - } - - private int scaleTrackballY(float yRate, int height) { - int yMove = (int) (yRate / TRACKBALL_SCALE * height); - int nextYMove = yMove; - if (yMove > 0) { - if (yMove > mTrackballYMove) { - yMove -= mTrackballYMove; - } - } else if (yMove < mTrackballYMove) { - yMove -= mTrackballYMove; - } - mTrackballYMove = nextYMove; - return yMove; - } - - private int keyCodeToSoundsEffect(int keyCode) { - switch(keyCode) { - case KeyEvent.KEYCODE_DPAD_UP: - return SoundEffectConstants.NAVIGATION_UP; - case KeyEvent.KEYCODE_DPAD_RIGHT: - return SoundEffectConstants.NAVIGATION_RIGHT; - case KeyEvent.KEYCODE_DPAD_DOWN: - return SoundEffectConstants.NAVIGATION_DOWN; - case KeyEvent.KEYCODE_DPAD_LEFT: - return SoundEffectConstants.NAVIGATION_LEFT; - } - return 0; - } - - private void doTrackball(long time, int metaState) { - int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime); - if (elapsed == 0) { - elapsed = TRACKBALL_TIMEOUT; - } - float xRate = mTrackballRemainsX * 1000 / elapsed; - float yRate = mTrackballRemainsY * 1000 / elapsed; - int viewWidth = getViewWidth(); - int viewHeight = getViewHeight(); - float ax = Math.abs(xRate); - float ay = Math.abs(yRate); - float maxA = Math.max(ax, ay); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doTrackball elapsed=" + elapsed - + " xRate=" + xRate - + " yRate=" + yRate - + " mTrackballRemainsX=" + mTrackballRemainsX - + " mTrackballRemainsY=" + mTrackballRemainsY); - } - int width = mContentWidth - viewWidth; - int height = mContentHeight - viewHeight; - if (width < 0) width = 0; - if (height < 0) height = 0; - ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER); - ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER); - maxA = Math.max(ax, ay); - int count = Math.max(0, (int) maxA); - int oldScrollX = getScrollX(); - int oldScrollY = getScrollY(); - if (count > 0) { - int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ? - KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN : - mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT : - KeyEvent.KEYCODE_DPAD_RIGHT; - count = Math.min(count, TRACKBALL_MOVE_COUNT); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode - + " count=" + count - + " mTrackballRemainsX=" + mTrackballRemainsX - + " mTrackballRemainsY=" + mTrackballRemainsY); - } - if (mNativeClass != 0) { - for (int i = 0; i < count; i++) { - letPageHandleNavKey(selectKeyCode, time, true, metaState); - } - letPageHandleNavKey(selectKeyCode, time, false, metaState); - } - mTrackballRemainsX = mTrackballRemainsY = 0; - } - if (count >= TRACKBALL_SCROLL_COUNT) { - int xMove = scaleTrackballX(xRate, width); - int yMove = scaleTrackballY(yRate, height); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doTrackball pinScrollBy" - + " count=" + count - + " xMove=" + xMove + " yMove=" + yMove - + " mScrollX-oldScrollX=" + (getScrollX()-oldScrollX) - + " mScrollY-oldScrollY=" + (getScrollY()-oldScrollY) - ); - } - if (Math.abs(getScrollX() - oldScrollX) > Math.abs(xMove)) { - xMove = 0; - } - if (Math.abs(getScrollY() - oldScrollY) > Math.abs(yMove)) { - yMove = 0; - } - if (xMove != 0 || yMove != 0) { - pinScrollBy(xMove, yMove, true, 0); - } - } - } - - /** - * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}. - * @return Maximum horizontal scroll position within real content - */ - int computeMaxScrollX() { - return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0); - } - - /** - * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}. - * @return Maximum vertical scroll position within real content - */ - int computeMaxScrollY() { - return Math.max(computeRealVerticalScrollRange() + getTitleHeight() - - getViewHeightWithTitle(), 0); - } - - boolean updateScrollCoordinates(int x, int y) { - int oldX = getScrollX(); - int oldY = getScrollY(); - setScrollXRaw(x); - setScrollYRaw(y); - if (oldX != getScrollX() || oldY != getScrollY()) { - mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY); - return true; - } else { - return false; - } - } - - @Override - public void flingScroll(int vx, int vy) { - mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0, - computeMaxScrollY(), mOverflingDistance, mOverflingDistance); - invalidate(); - } - - private void doFling() { - if (mVelocityTracker == null) { - return; - } - int maxX = computeMaxScrollX(); - int maxY = computeMaxScrollY(); - - mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling); - int vx = (int) mVelocityTracker.getXVelocity(); - int vy = (int) mVelocityTracker.getYVelocity(); - - int scrollX = getScrollX(); - int scrollY = getScrollY(); - int overscrollDistance = mOverscrollDistance; - int overflingDistance = mOverflingDistance; - - // Use the layer's scroll data if applicable. - if (mTouchMode == TOUCH_DRAG_LAYER_MODE) { - scrollX = mScrollingLayerRect.left; - scrollY = mScrollingLayerRect.top; - maxX = mScrollingLayerRect.right; - maxY = mScrollingLayerRect.bottom; - // No overscrolling for layers. - overscrollDistance = overflingDistance = 0; - } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) { - scrollX = getTextScrollX(); - scrollY = getTextScrollY(); - maxX = getMaxTextScrollX(); - maxY = getMaxTextScrollY(); - // No overscrolling for edit text. - overscrollDistance = overflingDistance = 0; - } - - if (mSnapScrollMode != SNAP_NONE) { - if ((mSnapScrollMode & SNAP_X) == SNAP_X) { - vy = 0; - } else { - vx = 0; - } - } - if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) { - WebViewCore.resumePriority(); - if (!mSelectingText) { - WebViewCore.resumeUpdatePicture(mWebViewCore); - } - if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) { - invalidate(); - } - return; - } - float currentVelocity = mScroller.getCurrVelocity(); - float velocity = (float) Math.hypot(vx, vy); - if (mLastVelocity > 0 && currentVelocity > 0 && velocity - > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) { - float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX) - - Math.atan2(vy, vx))); - final float circle = (float) (Math.PI) * 2.0f; - if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) { - vx += currentVelocity * mLastVelX / mLastVelocity; - vy += currentVelocity * mLastVelY / mLastVelocity; - velocity = (float) Math.hypot(vx, vy); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy); - } - } else if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doFling missed " + deltaR / circle); - } - } else if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "doFling start last=" + mLastVelocity - + " current=" + currentVelocity - + " vx=" + vx + " vy=" + vy - + " maxX=" + maxX + " maxY=" + maxY - + " scrollX=" + scrollX + " scrollY=" + scrollY - + " layer=" + mCurrentScrollingLayerId); - } - - // Allow sloppy flings without overscrolling at the edges. - if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) { - vx = 0; - } - if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) { - vy = 0; - } - - if (overscrollDistance < overflingDistance) { - if ((vx > 0 && scrollX == -overscrollDistance) || - (vx < 0 && scrollX == maxX + overscrollDistance)) { - vx = 0; - } - if ((vy > 0 && scrollY == -overscrollDistance) || - (vy < 0 && scrollY == maxY + overscrollDistance)) { - vy = 0; - } - } - - mLastVelX = vx; - mLastVelY = vy; - mLastVelocity = velocity; - - // no horizontal overscroll if the content just fits - mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY, - maxX == 0 ? 0 : overflingDistance, overflingDistance); - - invalidate(); - } - - /** - * See {@link WebView#getZoomControls()} - */ - @Override - @Deprecated - public View getZoomControls() { - if (!getSettings().supportZoom()) { - Log.w(LOGTAG, "This WebView doesn't support zoom."); - return null; - } - return mZoomManager.getExternalZoomPicker(); - } - - void dismissZoomControl() { - mZoomManager.dismissZoomPicker(); - } - - float getDefaultZoomScale() { - return mZoomManager.getDefaultScale(); - } - - /** - * Return the overview scale of the WebView - * @return The overview scale. - */ - float getZoomOverviewScale() { - return mZoomManager.getZoomOverviewScale(); - } - - /** - * See {@link WebView#canZoomIn()} - */ - @Override - public boolean canZoomIn() { - return mZoomManager.canZoomIn(); - } - - /** - * See {@link WebView#canZoomOut()} - */ - @Override - public boolean canZoomOut() { - return mZoomManager.canZoomOut(); - } - - /** - * See {@link WebView#zoomIn()} - */ - @Override - public boolean zoomIn() { - return mZoomManager.zoomIn(); - } - - /** - * See {@link WebView#zoomOut()} - */ - @Override - public boolean zoomOut() { - return mZoomManager.zoomOut(); - } - - /* - * Return true if the rect (e.g. plugin) is fully visible and maximized - * inside the WebView. - */ - boolean isRectFitOnScreen(Rect rect) { - final int rectWidth = rect.width(); - final int rectHeight = rect.height(); - final int viewWidth = getViewWidth(); - final int viewHeight = getViewHeightWithTitle(); - float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight); - scale = mZoomManager.computeScaleWithLimits(scale); - return !mZoomManager.willScaleTriggerZoom(scale) - && contentToViewX(rect.left) >= getScrollX() - && contentToViewX(rect.right) <= getScrollX() + viewWidth - && contentToViewY(rect.top) >= getScrollY() - && contentToViewY(rect.bottom) <= getScrollY() + viewHeight; - } - - /* - * Maximize and center the rectangle, specified in the document coordinate - * space, inside the WebView. If the zoom doesn't need to be changed, do an - * animated scroll to center it. If the zoom needs to be changed, find the - * zoom center and do a smooth zoom transition. The rect is in document - * coordinates - */ - void centerFitRect(Rect rect) { - final int rectWidth = rect.width(); - final int rectHeight = rect.height(); - final int viewWidth = getViewWidth(); - final int viewHeight = getViewHeightWithTitle(); - float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight - / rectHeight); - scale = mZoomManager.computeScaleWithLimits(scale); - if (!mZoomManager.willScaleTriggerZoom(scale)) { - pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2, - contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2, - true, 0); - } else { - float actualScale = mZoomManager.getScale(); - float oldScreenX = rect.left * actualScale - getScrollX(); - float rectViewX = rect.left * scale; - float rectViewWidth = rectWidth * scale; - float newMaxWidth = mContentWidth * scale; - float newScreenX = (viewWidth - rectViewWidth) / 2; - // pin the newX to the WebView - if (newScreenX > rectViewX) { - newScreenX = rectViewX; - } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) { - newScreenX = viewWidth - (newMaxWidth - rectViewX); - } - float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale) - / (scale - actualScale); - float oldScreenY = rect.top * actualScale + getTitleHeight() - - getScrollY(); - float rectViewY = rect.top * scale + getTitleHeight(); - float rectViewHeight = rectHeight * scale; - float newMaxHeight = mContentHeight * scale + getTitleHeight(); - float newScreenY = (viewHeight - rectViewHeight) / 2; - // pin the newY to the WebView - if (newScreenY > rectViewY) { - newScreenY = rectViewY; - } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) { - newScreenY = viewHeight - (newMaxHeight - rectViewY); - } - float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale) - / (scale - actualScale); - mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY); - mZoomManager.startZoomAnimation(scale, false); - } - } - - // Called by JNI to handle a touch on a node representing an email address, - // address, or phone number - private void overrideLoading(String url) { - mCallbackProxy.uiOverrideUrlLoading(url); - } - - @Override - public boolean requestFocus(int direction, Rect previouslyFocusedRect) { - // Check if we are destroyed - if (mWebViewCore == null) return false; - // FIXME: If a subwindow is showing find, and the user touches the - // background window, it can steal focus. - if (mFindIsUp) return false; - boolean result = false; - result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect); - if (mWebViewCore.getSettings().getNeedInitialFocus() - && !mWebView.isInTouchMode()) { - // For cases such as GMail, where we gain focus from a direction, - // we want to move to the first available link. - // FIXME: If there are no visible links, we may not want to - int fakeKeyDirection = 0; - switch(direction) { - case View.FOCUS_UP: - fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP; - break; - case View.FOCUS_DOWN: - fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN; - break; - case View.FOCUS_LEFT: - fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT; - break; - case View.FOCUS_RIGHT: - fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT; - break; - default: - return result; - } - mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection); - } - return result; - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - - int measuredHeight = heightSize; - int measuredWidth = widthSize; - - // Grab the content size from WebViewCore. - int contentHeight = contentToViewDimension(mContentHeight); - int contentWidth = contentToViewDimension(mContentWidth); - -// Log.d(LOGTAG, "------- measure " + heightMode); - - if (heightMode != MeasureSpec.EXACTLY) { - mHeightCanMeasure = true; - measuredHeight = contentHeight; - if (heightMode == MeasureSpec.AT_MOST) { - // If we are larger than the AT_MOST height, then our height can - // no longer be measured and we should scroll internally. - if (measuredHeight > heightSize) { - measuredHeight = heightSize; - mHeightCanMeasure = false; - measuredHeight |= View.MEASURED_STATE_TOO_SMALL; - } - } - } else { - mHeightCanMeasure = false; - } - if (mNativeClass != 0) { - nativeSetHeightCanMeasure(mHeightCanMeasure); - } - // For the width, always use the given size unless unspecified. - if (widthMode == MeasureSpec.UNSPECIFIED) { - mWidthCanMeasure = true; - measuredWidth = contentWidth; - } else { - if (measuredWidth < contentWidth) { - measuredWidth |= View.MEASURED_STATE_TOO_SMALL; - } - mWidthCanMeasure = false; - } - - synchronized (this) { - mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight); - } - } - - @Override - public boolean requestChildRectangleOnScreen(View child, - Rect rect, - boolean immediate) { - if (mNativeClass == 0) { - return false; - } - // don't scroll while in zoom animation. When it is done, we will adjust - // the necessary components - if (mZoomManager.isFixedLengthAnimationInProgress()) { - return false; - } - - rect.offset(child.getLeft() - child.getScrollX(), - child.getTop() - child.getScrollY()); - - Rect content = new Rect(viewToContentX(getScrollX()), - viewToContentY(getScrollY()), - viewToContentX(getScrollX() + getWidth() - - mWebView.getVerticalScrollbarWidth()), - viewToContentY(getScrollY() + getViewHeightWithTitle())); - int screenTop = contentToViewY(content.top); - int screenBottom = contentToViewY(content.bottom); - int height = screenBottom - screenTop; - int scrollYDelta = 0; - - if (rect.bottom > screenBottom) { - int oneThirdOfScreenHeight = height / 3; - if (rect.height() > 2 * oneThirdOfScreenHeight) { - // If the rectangle is too tall to fit in the bottom two thirds - // of the screen, place it at the top. - scrollYDelta = rect.top - screenTop; - } else { - // If the rectangle will still fit on screen, we want its - // top to be in the top third of the screen. - scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight); - } - } else if (rect.top < screenTop) { - scrollYDelta = rect.top - screenTop; - } - - int screenLeft = contentToViewX(content.left); - int screenRight = contentToViewX(content.right); - int width = screenRight - screenLeft; - int scrollXDelta = 0; - - if (rect.right > screenRight && rect.left > screenLeft) { - if (rect.width() > width) { - scrollXDelta += (rect.left - screenLeft); - } else { - scrollXDelta += (rect.right - screenRight); - } - } else if (rect.left < screenLeft) { - scrollXDelta -= (screenLeft - rect.left); - } - - if ((scrollYDelta | scrollXDelta) != 0) { - return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0); - } - - return false; - } - - /* package */ void replaceTextfieldText(int oldStart, int oldEnd, - String replace, int newStart, int newEnd) { - WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData(); - arg.mReplace = replace; - arg.mNewStart = newStart; - arg.mNewEnd = newEnd; - mTextGeneration++; - arg.mTextGeneration = mTextGeneration; - sendBatchableInputMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg); - } - - /* package */ void passToJavaScript(String currentText, KeyEvent event) { - // check if mWebViewCore has been destroyed - if (mWebViewCore == null) { - return; - } - WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData(); - arg.mEvent = event; - arg.mCurrentText = currentText; - // Increase our text generation number, and pass it to webcore thread - mTextGeneration++; - mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg); - // WebKit's document state is not saved until about to leave the page. - // To make sure the host application, like Browser, has the up to date - // document state when it goes to background, we force to save the - // document state. - mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE); - mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE, null, 1000); - } - - public synchronized WebViewCore getWebViewCore() { - return mWebViewCore; - } - - private boolean canTextScroll(int directionX, int directionY) { - int scrollX = getTextScrollX(); - int scrollY = getTextScrollY(); - int maxScrollX = getMaxTextScrollX(); - int maxScrollY = getMaxTextScrollY(); - boolean canScrollX = (directionX > 0) - ? (scrollX < maxScrollX) - : (scrollX > 0); - boolean canScrollY = (directionY > 0) - ? (scrollY < maxScrollY) - : (scrollY > 0); - return canScrollX || canScrollY; - } - - private int getTextScrollX() { - return -mEditTextContent.left; - } - - private int getTextScrollY() { - return -mEditTextContent.top; - } - - private int getMaxTextScrollX() { - return Math.max(0, mEditTextContent.width() - mEditTextContentBounds.width()); - } - - private int getMaxTextScrollY() { - return Math.max(0, mEditTextContent.height() - mEditTextContentBounds.height()); - } - - //------------------------------------------------------------------------- - // Methods can be called from a separate thread, like WebViewCore - // If it needs to call the View system, it has to send message. - //------------------------------------------------------------------------- - - /** - * General handler to receive message coming from webkit thread - */ - class PrivateHandler extends Handler implements WebViewInputDispatcher.UiCallbacks { - @Override - public void handleMessage(Message msg) { - // exclude INVAL_RECT_MSG_ID since it is frequently output - if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) { - if (msg.what >= FIRST_PRIVATE_MSG_ID - && msg.what <= LAST_PRIVATE_MSG_ID) { - Log.v(LOGTAG, HandlerPrivateDebugString[msg.what - - FIRST_PRIVATE_MSG_ID]); - } else if (msg.what >= FIRST_PACKAGE_MSG_ID - && msg.what <= LAST_PACKAGE_MSG_ID) { - Log.v(LOGTAG, HandlerPackageDebugString[msg.what - - FIRST_PACKAGE_MSG_ID]); - } else { - Log.v(LOGTAG, Integer.toString(msg.what)); - } - } - if (mWebViewCore == null) { - // after WebView's destroy() is called, skip handling messages. - return; - } - if (mBlockWebkitViewMessages - && msg.what != WEBCORE_INITIALIZED_MSG_ID) { - // Blocking messages from webkit - return; - } - switch (msg.what) { - case REMEMBER_PASSWORD: { - mDatabase.setUsernamePassword( - msg.getData().getString("host"), - msg.getData().getString("username"), - msg.getData().getString("password")); - ((Message) msg.obj).sendToTarget(); - break; - } - case NEVER_REMEMBER_PASSWORD: { - mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null); - ((Message) msg.obj).sendToTarget(); - break; - } - case SCROLL_SELECT_TEXT: { - if (mAutoScrollX == 0 && mAutoScrollY == 0) { - mSentAutoScrollMessage = false; - break; - } - if (mCurrentScrollingLayerId == 0) { - pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0); - } else { - scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX, - mScrollingLayerRect.top + mAutoScrollY); - } - sendEmptyMessageDelayed( - SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL); - break; - } - case SCROLL_TO_MSG_ID: { - // arg1 = animate, arg2 = onlyIfImeIsShowing - // obj = Point(x, y) - if (msg.arg2 == 1) { - // This scroll is intended to bring the textfield into - // view, but is only necessary if the IME is showing - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm == null || !imm.isAcceptingText() - || !imm.isActive(mWebView)) { - break; - } - } - final Point p = (Point) msg.obj; - contentScrollTo(p.x, p.y, msg.arg1 == 1); - break; - } - case UPDATE_ZOOM_RANGE: { - WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj; - // mScrollX contains the new minPrefWidth - mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX); - break; - } - case UPDATE_ZOOM_DENSITY: { - final float density = (Float) msg.obj; - mZoomManager.updateDefaultZoomDensity(density); - break; - } - case NEW_PICTURE_MSG_ID: { - // called for new content - final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj; - setNewPicture(draw, true); - break; - } - case WEBCORE_INITIALIZED_MSG_ID: - // nativeCreate sets mNativeClass to a non-zero value - String drawableDir = BrowserFrame.getRawResFilename( - BrowserFrame.DRAWABLEDIR, mContext); - nativeCreate(msg.arg1, drawableDir, ActivityManager.isHighEndGfx()); - if (mDelaySetPicture != null) { - setNewPicture(mDelaySetPicture, true); - mDelaySetPicture = null; - } - if (mIsPaused) { - nativeSetPauseDrawing(mNativeClass, true); - } - mInputDispatcher = new WebViewInputDispatcher(this, - mWebViewCore.getInputDispatcherCallbacks()); - break; - case UPDATE_TEXTFIELD_TEXT_MSG_ID: - // Make sure that the textfield is currently focused - // and representing the same node as the pointer. - if (msg.arg2 == mTextGeneration) { - String text = (String) msg.obj; - if (null == text) { - text = ""; - } - if (mInputConnection != null && - mFieldPointer == msg.arg1) { - mInputConnection.setTextAndKeepSelection(text); - } - } - break; - case UPDATE_TEXT_SELECTION_MSG_ID: - updateTextSelectionFromMessage(msg.arg1, msg.arg2, - (WebViewCore.TextSelectionData) msg.obj); - break; - case TAKE_FOCUS: - int direction = msg.arg1; - View focusSearch = mWebView.focusSearch(direction); - if (focusSearch != null && focusSearch != mWebView) { - focusSearch.requestFocus(); - } - break; - case CLEAR_TEXT_ENTRY: - hideSoftKeyboard(); - break; - case INVAL_RECT_MSG_ID: { - Rect r = (Rect)msg.obj; - if (r == null) { - invalidate(); - } else { - // we need to scale r from content into view coords, - // which viewInvalidate() does for us - viewInvalidate(r.left, r.top, r.right, r.bottom); - } - break; - } - case REQUEST_FORM_DATA: - if (mFieldPointer == msg.arg1) { - ArrayAdapter<String> adapter = (ArrayAdapter<String>)msg.obj; - mAutoCompletePopup.setAdapter(adapter); - } - break; - - case LONG_PRESS_CENTER: - // as this is shared by keydown and trackballdown, reset all - // the states - mGotCenterDown = false; - mTrackballDown = false; - mWebView.performLongClick(); - break; - - case WEBCORE_NEED_TOUCH_EVENTS: - mInputDispatcher.setWebKitWantsTouchEvents(msg.arg1 != 0); - break; - - case REQUEST_KEYBOARD: - if (msg.arg1 == 0) { - hideSoftKeyboard(); - } else { - displaySoftKeyboard(false); - } - break; - - case DRAG_HELD_MOTIONLESS: - mHeldMotionless = MOTIONLESS_TRUE; - invalidate(); - break; - - case SCREEN_ON: - mWebView.setKeepScreenOn(msg.arg1 == 1); - break; - - case EXIT_FULLSCREEN_VIDEO: - if (mHTML5VideoViewProxy != null) { - mHTML5VideoViewProxy.exitFullScreenVideo(); - } - break; - - case SHOW_FULLSCREEN: { - View view = (View) msg.obj; - int orientation = msg.arg1; - int npp = msg.arg2; - - if (inFullScreenMode()) { - Log.w(LOGTAG, "Should not have another full screen."); - dismissFullScreenMode(); - } - mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp); - mFullScreenHolder.setContentView(view); - mFullScreenHolder.show(); - invalidate(); - - break; - } - case HIDE_FULLSCREEN: - dismissFullScreenMode(); - break; - - case SHOW_RECT_MSG_ID: { - WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj; - int left = contentToViewX(data.mLeft); - int width = contentToViewDimension(data.mWidth); - int maxWidth = contentToViewDimension(data.mContentWidth); - int viewWidth = getViewWidth(); - int x = (int) (left + data.mXPercentInDoc * width - - data.mXPercentInView * viewWidth); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" + - width + ",maxWidth=" + maxWidth + - ",viewWidth=" + viewWidth + ",x=" - + x + ",xPercentInDoc=" + data.mXPercentInDoc + - ",xPercentInView=" + data.mXPercentInView+ ")"); - } - // use the passing content width to cap x as the current - // mContentWidth may not be updated yet - x = Math.max(0, - (Math.min(maxWidth, x + viewWidth)) - viewWidth); - int top = contentToViewY(data.mTop); - int height = contentToViewDimension(data.mHeight); - int maxHeight = contentToViewDimension(data.mContentHeight); - int viewHeight = getViewHeight(); - int y = (int) (top + data.mYPercentInDoc * height - - data.mYPercentInView * viewHeight); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" + - height + ",maxHeight=" + maxHeight + - ",viewHeight=" + viewHeight + ",y=" - + y + ",yPercentInDoc=" + data.mYPercentInDoc + - ",yPercentInView=" + data.mYPercentInView+ ")"); - } - // use the passing content height to cap y as the current - // mContentHeight may not be updated yet - y = Math.max(0, - (Math.min(maxHeight, y + viewHeight) - viewHeight)); - // We need to take into account the visible title height - // when scrolling since y is an absolute view position. - y = Math.max(0, y - getVisibleTitleHeightImpl()); - mWebView.scrollTo(x, y); - } - break; - - case CENTER_FIT_RECT: - centerFitRect((Rect)msg.obj); - break; - - case SET_SCROLLBAR_MODES: - mHorizontalScrollBarMode = msg.arg1; - mVerticalScrollBarMode = msg.arg2; - break; - - case FOCUS_NODE_CHANGED: - mIsEditingText = (msg.arg1 == mFieldPointer); - if (mAutoCompletePopup != null && !mIsEditingText) { - mAutoCompletePopup.clearAdapter(); - } - // fall through to HIT_TEST_RESULT - case HIT_TEST_RESULT: - WebKitHitTest hit = (WebKitHitTest) msg.obj; - mFocusedNode = hit; - setTouchHighlightRects(hit); - setHitTestResult(hit); - break; - - case SAVE_WEBARCHIVE_FINISHED: - SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj; - if (saveMessage.mCallback != null) { - saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile); - } - break; - - case SET_AUTOFILLABLE: - mAutoFillData = (WebViewCore.AutoFillData) msg.obj; - if (mInputConnection != null) { - mInputConnection.setAutoFillable(mAutoFillData.getQueryId()); - mAutoCompletePopup.setAutoFillQueryId(mAutoFillData.getQueryId()); - } - break; - - case AUTOFILL_COMPLETE: - if (mAutoCompletePopup != null) { - ArrayList<String> pastEntries = new ArrayList<String>(); - mAutoCompletePopup.setAdapter(new ArrayAdapter<String>( - mContext, - com.android.internal.R.layout.web_text_view_dropdown, - pastEntries)); - } - break; - - case COPY_TO_CLIPBOARD: - copyToClipboard((String) msg.obj); - break; - - case INIT_EDIT_FIELD: - if (mInputConnection != null) { - TextFieldInitData initData = (TextFieldInitData) msg.obj; - mTextGeneration = 0; - mFieldPointer = initData.mFieldPointer; - mInputConnection.initEditorInfo(initData); - mInputConnection.setTextAndKeepSelection(initData.mText); - mEditTextContentBounds.set(initData.mContentBounds); - mEditTextLayerId = initData.mNodeLayerId; - nativeMapLayerRect(mNativeClass, mEditTextLayerId, - mEditTextContentBounds); - mEditTextContent.set(initData.mClientRect); - relocateAutoCompletePopup(); - } - break; - - case REPLACE_TEXT:{ - String text = (String)msg.obj; - int start = msg.arg1; - int end = msg.arg2; - int cursorPosition = start + text.length(); - replaceTextfieldText(start, end, text, - cursorPosition, cursorPosition); - selectionDone(); - break; - } - - case UPDATE_MATCH_COUNT: { - WebViewCore.FindAllRequest request = (WebViewCore.FindAllRequest)msg.obj; - if (request == null) { - if (mFindCallback != null) { - mFindCallback.updateMatchCount(0, 0, true); - } - } else if (request == mFindRequest) { - int matchCount, matchIndex; - synchronized (mFindRequest) { - matchCount = request.mMatchCount; - matchIndex = request.mMatchIndex; - } - if (mFindCallback != null) { - mFindCallback.updateMatchCount(matchIndex, matchCount, false); - } - if (mFindListener != null) { - mFindListener.onFindResultReceived(matchIndex, matchCount, true); - } - } - break; - } - - case CLEAR_CARET_HANDLE: - if (mIsCaretSelection) { - selectionDone(); - } - break; - - case KEY_PRESS: - sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null); - break; - - case RELOCATE_AUTO_COMPLETE_POPUP: - relocateAutoCompletePopup(); - break; - - case AUTOFILL_FORM: - mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, - msg.arg1, /* unused */0); - break; - - case EDIT_TEXT_SIZE_CHANGED: - if (msg.arg1 == mFieldPointer) { - mEditTextContent.set((Rect)msg.obj); - } - break; - - case SHOW_CARET_HANDLE: - if (!mSelectingText && mIsEditingText && mIsCaretSelection) { - setupWebkitSelect(); - resetCaretTimer(); - showPasteWindow(); - } - break; - - case UPDATE_CONTENT_BOUNDS: - mEditTextContentBounds.set((Rect) msg.obj); - nativeMapLayerRect(mNativeClass, mEditTextLayerId, - mEditTextContentBounds); - break; - - case SCROLL_EDIT_TEXT: - scrollEditWithCursor(); - break; - - case SCROLL_HANDLE_INTO_VIEW: - scrollDraggedSelectionHandleIntoView(); - break; - - default: - super.handleMessage(msg); - break; - } - } - - @Override - public Looper getUiLooper() { - return getLooper(); - } - - @Override - public void dispatchUiEvent(MotionEvent event, int eventType, int flags) { - onHandleUiEvent(event, eventType, flags); - } - - @Override - public Context getContext() { - return WebViewClassic.this.getContext(); - } - - @Override - public boolean shouldInterceptTouchEvent(MotionEvent event) { - if (!mSelectingText) { - return false; - } - ensureSelectionHandles(); - int y = Math.round(event.getY() - getTitleHeight() + getScrollY()); - int x = Math.round(event.getX() + getScrollX()); - boolean isPressingHandle; - if (mIsCaretSelection) { - isPressingHandle = mSelectHandleCenter.getBounds() - .contains(x, y); - } else { - isPressingHandle = - mSelectHandleBaseBounds.contains(x, y) - || mSelectHandleExtentBounds.contains(x, y); - } - return isPressingHandle; - } - - @Override - public void showTapHighlight(boolean show) { - if (mShowTapHighlight != show) { - mShowTapHighlight = show; - invalidate(); - } - } - - @Override - public void clearPreviousHitTest() { - setHitTestResult(null); - } - } - - private void setHitTestTypeFromUrl(String url) { - String substr = null; - if (url.startsWith(SCHEME_GEO)) { - mInitialHitTestResult.setType(HitTestResult.GEO_TYPE); - substr = url.substring(SCHEME_GEO.length()); - } else if (url.startsWith(SCHEME_TEL)) { - mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE); - substr = url.substring(SCHEME_TEL.length()); - } else if (url.startsWith(SCHEME_MAILTO)) { - mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE); - substr = url.substring(SCHEME_MAILTO.length()); - } else { - mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE); - mInitialHitTestResult.setExtra(url); - return; - } - try { - mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8")); - } catch (Throwable e) { - Log.w(LOGTAG, "Failed to decode URL! " + substr, e); - mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE); - } - } - - private void setHitTestResult(WebKitHitTest hit) { - if (hit == null) { - mInitialHitTestResult = null; - return; - } - mInitialHitTestResult = new HitTestResult(); - if (hit.mLinkUrl != null) { - setHitTestTypeFromUrl(hit.mLinkUrl); - if (hit.mImageUrl != null - && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) { - mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE); - mInitialHitTestResult.setExtra(hit.mImageUrl); - } - } else if (hit.mImageUrl != null) { - mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE); - mInitialHitTestResult.setExtra(hit.mImageUrl); - } else if (hit.mEditable) { - mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE); - } else if (hit.mIntentUrl != null) { - setHitTestTypeFromUrl(hit.mIntentUrl); - } - } - - private boolean shouldDrawHighlightRect() { - if (mFocusedNode == null || mInitialHitTestResult == null) { - return false; - } - if (mTouchHighlightRegion.isEmpty()) { - return false; - } - if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) { - return mDrawCursorRing && !mFocusedNode.mEditable; - } - if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) { - return false; - } - return mShowTapHighlight; - } - - - private FocusTransitionDrawable mFocusTransition = null; - static class FocusTransitionDrawable extends Drawable { - Region mPreviousRegion; - Region mNewRegion; - float mProgress = 0; - WebViewClassic mWebView; - Paint mPaint; - int mMaxAlpha; - Point mTranslate; - - public FocusTransitionDrawable(WebViewClassic view) { - mWebView = view; - mPaint = new Paint(mWebView.mTouchHightlightPaint); - mMaxAlpha = mPaint.getAlpha(); - } - - @Override - public void setColorFilter(ColorFilter cf) { - } - - @Override - public void setAlpha(int alpha) { - } - - @Override - public int getOpacity() { - return 0; - } - - public void setProgress(float p) { - mProgress = p; - if (mWebView.mFocusTransition == this) { - if (mProgress == 1f) - mWebView.mFocusTransition = null; - mWebView.invalidate(); - } - } - - public float getProgress() { - return mProgress; - } - - @Override - public void draw(Canvas canvas) { - if (mTranslate == null) { - Rect bounds = mPreviousRegion.getBounds(); - Point from = new Point(bounds.centerX(), bounds.centerY()); - mNewRegion.getBounds(bounds); - Point to = new Point(bounds.centerX(), bounds.centerY()); - mTranslate = new Point(from.x - to.x, from.y - to.y); - } - int alpha = (int) (mProgress * mMaxAlpha); - RegionIterator iter = new RegionIterator(mPreviousRegion); - Rect r = new Rect(); - mPaint.setAlpha(mMaxAlpha - alpha); - float tx = mTranslate.x * mProgress; - float ty = mTranslate.y * mProgress; - int save = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(-tx, -ty); - while (iter.next(r)) { - canvas.drawRect(r, mPaint); - } - canvas.restoreToCount(save); - iter = new RegionIterator(mNewRegion); - r = new Rect(); - mPaint.setAlpha(alpha); - save = canvas.save(Canvas.MATRIX_SAVE_FLAG); - tx = mTranslate.x - tx; - ty = mTranslate.y - ty; - canvas.translate(tx, ty); - while (iter.next(r)) { - canvas.drawRect(r, mPaint); - } - canvas.restoreToCount(save); - } - }; - - private boolean shouldAnimateTo(WebKitHitTest hit) { - // TODO: Don't be annoying or throw out the animation entirely - return false; - } - - private void setTouchHighlightRects(WebKitHitTest hit) { - FocusTransitionDrawable transition = null; - if (shouldAnimateTo(hit)) { - transition = new FocusTransitionDrawable(this); - } - Rect[] rects = hit != null ? hit.mTouchRects : null; - if (!mTouchHighlightRegion.isEmpty()) { - mWebView.invalidate(mTouchHighlightRegion.getBounds()); - if (transition != null) { - transition.mPreviousRegion = new Region(mTouchHighlightRegion); - } - mTouchHighlightRegion.setEmpty(); - } - if (rects != null) { - mTouchHightlightPaint.setColor(hit.mTapHighlightColor); - for (Rect rect : rects) { - Rect viewRect = contentToViewRect(rect); - // some sites, like stories in nytimes.com, set - // mouse event handler in the top div. It is not - // user friendly to highlight the div if it covers - // more than half of the screen. - if (viewRect.width() < getWidth() >> 1 - || viewRect.height() < getHeight() >> 1) { - mTouchHighlightRegion.union(viewRect); - } else if (DebugFlags.WEB_VIEW) { - Log.d(LOGTAG, "Skip the huge selection rect:" - + viewRect); - } - } - mWebView.invalidate(mTouchHighlightRegion.getBounds()); - if (transition != null && transition.mPreviousRegion != null) { - transition.mNewRegion = new Region(mTouchHighlightRegion); - mFocusTransition = transition; - ObjectAnimator animator = ObjectAnimator.ofFloat( - mFocusTransition, "progress", 1f); - animator.start(); - } - } - } - - // Interface to allow the profiled WebView to hook the page swap notifications. - public interface PageSwapDelegate { - void onPageSwapOccurred(boolean notifyAnimationStarted); - } - - long mLastSwapTime; - double mAverageSwapFps; - - /** Called by JNI when pages are swapped (only occurs with hardware - * acceleration) */ - protected void pageSwapCallback(boolean notifyAnimationStarted) { - if (DebugFlags.MEASURE_PAGE_SWAP_FPS) { - long now = System.currentTimeMillis(); - long diff = now - mLastSwapTime; - mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2; - Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps); - mLastSwapTime = now; - } - mWebViewCore.resumeWebKitDraw(); - if (notifyAnimationStarted) { - mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); - } - if (mWebView instanceof PageSwapDelegate) { - // This provides a hook for ProfiledWebView to observe the tile page swaps. - ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted); - } - - if (mPictureListener != null) { - // trigger picture listener for hardware layers. Software layers are - // triggered in setNewPicture - Picture picture = mContext.getApplicationInfo().targetSdkVersion < - Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null; - if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture"); - mPictureListener.onNewPicture(getWebView(), picture); - } - } - - void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) { - if (mNativeClass == 0) { - if (mDelaySetPicture != null) { - throw new IllegalStateException("Tried to setNewPicture with" - + " a delay picture already set! (memory leak)"); - } - // Not initialized yet, delay set - mDelaySetPicture = draw; - return; - } - WebViewCore.ViewState viewState = draw.mViewState; - boolean isPictureAfterFirstLayout = viewState != null; - - if (updateBaseLayer) { - setBaseLayer(draw.mBaseLayer, - getSettings().getShowVisualIndicator(), - isPictureAfterFirstLayout); - } - final Point viewSize = draw.mViewSize; - // We update the layout (i.e. request a layout from the - // view system) if the last view size that we sent to - // WebCore matches the view size of the picture we just - // received in the fixed dimension. - final boolean updateLayout = viewSize.x == mLastWidthSent - && viewSize.y == mLastHeightSent; - // Don't send scroll event for picture coming from webkit, - // since the new picture may cause a scroll event to override - // the saved history scroll position. - mSendScrollEvent = false; - recordNewContentSize(draw.mContentSize.x, - draw.mContentSize.y, updateLayout); - if (isPictureAfterFirstLayout) { - // Reset the last sent data here since dealing with new page. - mLastWidthSent = 0; - mZoomManager.onFirstLayout(draw); - int scrollX = viewState.mShouldStartScrolledRight - ? getContentWidth() : viewState.mScrollX; - int scrollY = viewState.mScrollY; - contentScrollTo(scrollX, scrollY, false); - if (!mDrawHistory) { - // As we are on a new page, hide the keyboard - hideSoftKeyboard(); - } - } - mSendScrollEvent = true; - - int functor = 0; - boolean forceInval = isPictureAfterFirstLayout; - ViewRootImpl viewRoot = mWebView.getViewRootImpl(); - if (mWebView.isHardwareAccelerated() - && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE - && viewRoot != null) { - functor = nativeGetDrawGLFunction(mNativeClass); - if (functor != 0) { - // force an invalidate if functor attach not successful - forceInval |= !viewRoot.attachFunctor(functor); - } - } - - if (functor == 0 - || forceInval - || mWebView.getLayerType() != View.LAYER_TYPE_NONE) { - // invalidate the screen so that the next repaint will show new content - // TODO: partial invalidate - mWebView.invalidate(); - } - - // update the zoom information based on the new picture - if (mZoomManager.onNewPicture(draw)) - invalidate(); - - if (isPictureAfterFirstLayout) { - mViewManager.postReadyToDrawAll(); - } - scrollEditWithCursor(); - - if (mPictureListener != null) { - if (!mWebView.isHardwareAccelerated() - || mWebView.getLayerType() == View.LAYER_TYPE_SOFTWARE) { - // trigger picture listener for software layers. Hardware layers are - // triggered in pageSwapCallback - Picture picture = mContext.getApplicationInfo().targetSdkVersion < - Build.VERSION_CODES.JELLY_BEAN_MR2 ? capturePicture() : null; - if (DebugFlags.TRACE_CALLBACK) Log.d(CallbackProxy.LOGTAG, "onNewPicture"); - mPictureListener.onNewPicture(getWebView(), picture); - } - } - } - - /** - * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID - * and UPDATE_TEXT_SELECTION_MSG_ID. - */ - private void updateTextSelectionFromMessage(int nodePointer, - int textGeneration, WebViewCore.TextSelectionData data) { - if (textGeneration == mTextGeneration) { - if (mInputConnection != null && mFieldPointer == nodePointer) { - mInputConnection.setSelection(data.mStart, data.mEnd); - } - } - nativeSetTextSelection(mNativeClass, data.mSelectTextPtr); - - if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR) - || (!mSelectingText && data.mStart != data.mEnd - && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) { - selectionDone(); - mShowTextSelectionExtra = true; - invalidate(); - return; - } - - if (data.mSelectTextPtr != 0 && - (data.mStart != data.mEnd || - (mFieldPointer == nodePointer && mFieldPointer != 0) || - (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) { - mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0; - mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0); - if (mIsCaretSelection && - (mInputConnection == null || - mInputConnection.getEditable().length() == 0)) { - // There's no text, don't show caret handle. - selectionDone(); - } else { - if (!mSelectingText) { - setupWebkitSelect(); - } else { - syncSelectionCursors(); - } - animateHandles(); - if (mIsCaretSelection) { - resetCaretTimer(); - } - } - } else { - selectionDone(); - } - invalidate(); - } - - private void scrollEditText(int scrollX, int scrollY) { - // Scrollable edit text. Scroll it. - float maxScrollX = getMaxTextScrollX(); - float scrollPercentX = ((float)scrollX)/maxScrollX; - mEditTextContent.offsetTo(-scrollX, -scrollY); - mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT); - mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, - scrollY, (Float)scrollPercentX); - animateHandles(); - } - - private void beginTextBatch() { - mIsBatchingTextChanges = true; - } - - private void commitTextBatch() { - if (mWebViewCore != null) { - mWebViewCore.sendMessages(mBatchedTextChanges); - } - mBatchedTextChanges.clear(); - mIsBatchingTextChanges = false; - } - - void sendBatchableInputMessage(int what, int arg1, int arg2, - Object obj) { - if (mWebViewCore == null) { - return; - } - Message message = Message.obtain(null, what, arg1, arg2, obj); - if (mIsBatchingTextChanges) { - mBatchedTextChanges.add(message); - } else { - mWebViewCore.sendMessage(message); - } - } - - // Class used to use a dropdown for a <select> element - private class InvokeListBox implements Runnable { - // Whether the listbox allows multiple selection. - private boolean mMultiple; - // Passed in to a list with multiple selection to tell - // which items are selected. - private int[] mSelectedArray; - // Passed in to a list with single selection to tell - // where the initial selection is. - private int mSelection; - - private Container[] mContainers; - - // Need these to provide stable ids to my ArrayAdapter, - // which normally does not have stable ids. (Bug 1250098) - private class Container extends Object { - /** - * Possible values for mEnabled. Keep in sync with OptionStatus in - * WebViewCore.cpp - */ - final static int OPTGROUP = -1; - final static int OPTION_DISABLED = 0; - final static int OPTION_ENABLED = 1; - - String mString; - int mEnabled; - int mId; - - @Override - public String toString() { - return mString; - } - } - - /** - * Subclass ArrayAdapter so we can disable OptionGroupLabels, - * and allow filtering. - */ - private class MyArrayListAdapter extends ArrayAdapter<Container> { - public MyArrayListAdapter() { - super(WebViewClassic.this.mContext, - mMultiple ? com.android.internal.R.layout.select_dialog_multichoice : - com.android.internal.R.layout.webview_select_singlechoice, - mContainers); - } - - @Override - public View getView(int position, View convertView, - ViewGroup parent) { - // Always pass in null so that we will get a new CheckedTextView - // Otherwise, an item which was previously used as an <optgroup> - // element (i.e. has no check), could get used as an <option> - // element, which needs a checkbox/radio, but it would not have - // one. - convertView = super.getView(position, null, parent); - Container c = item(position); - if (c != null && Container.OPTION_ENABLED != c.mEnabled) { - // ListView does not draw dividers between disabled and - // enabled elements. Use a LinearLayout to provide dividers - LinearLayout layout = new LinearLayout(mContext); - layout.setOrientation(LinearLayout.VERTICAL); - if (position > 0) { - View dividerTop = new View(mContext); - dividerTop.setBackgroundResource( - android.R.drawable.divider_horizontal_bright); - layout.addView(dividerTop); - } - - if (Container.OPTGROUP == c.mEnabled) { - // Currently select_dialog_multichoice uses CheckedTextViews. - // If that changes, the class cast will no longer be valid. - if (mMultiple) { - Assert.assertTrue(convertView instanceof CheckedTextView); - ((CheckedTextView) convertView).setCheckMarkDrawable(null); - } - } else { - // c.mEnabled == Container.OPTION_DISABLED - // Draw the disabled element in a disabled state. - convertView.setEnabled(false); - } - - layout.addView(convertView); - if (position < getCount() - 1) { - View dividerBottom = new View(mContext); - dividerBottom.setBackgroundResource( - android.R.drawable.divider_horizontal_bright); - layout.addView(dividerBottom); - } - return layout; - } - return convertView; - } - - @Override - public boolean hasStableIds() { - // AdapterView's onChanged method uses this to determine whether - // to restore the old state. Return false so that the old (out - // of date) state does not replace the new, valid state. - return false; - } - - private Container item(int position) { - if (position < 0 || position >= getCount()) { - return null; - } - return getItem(position); - } - - @Override - public long getItemId(int position) { - Container item = item(position); - if (item == null) { - return -1; - } - return item.mId; - } - - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public boolean isEnabled(int position) { - Container item = item(position); - if (item == null) { - return false; - } - return Container.OPTION_ENABLED == item.mEnabled; - } - } - - private InvokeListBox(String[] array, int[] enabled, int[] selected) { - mMultiple = true; - mSelectedArray = selected; - - int length = array.length; - mContainers = new Container[length]; - for (int i = 0; i < length; i++) { - mContainers[i] = new Container(); - mContainers[i].mString = array[i]; - mContainers[i].mEnabled = enabled[i]; - mContainers[i].mId = i; - } - } - - private InvokeListBox(String[] array, int[] enabled, int selection) { - mSelection = selection; - mMultiple = false; - - int length = array.length; - mContainers = new Container[length]; - for (int i = 0; i < length; i++) { - mContainers[i] = new Container(); - mContainers[i].mString = array[i]; - mContainers[i].mEnabled = enabled[i]; - mContainers[i].mId = i; - } - } - - /* - * Whenever the data set changes due to filtering, this class ensures - * that the checked item remains checked. - */ - private class SingleDataSetObserver extends DataSetObserver { - private long mCheckedId; - private ListView mListView; - private Adapter mAdapter; - - /* - * Create a new observer. - * @param id The ID of the item to keep checked. - * @param l ListView for getting and clearing the checked states - * @param a Adapter for getting the IDs - */ - public SingleDataSetObserver(long id, ListView l, Adapter a) { - mCheckedId = id; - mListView = l; - mAdapter = a; - } - - @Override - public void onChanged() { - // The filter may have changed which item is checked. Find the - // item that the ListView thinks is checked. - int position = mListView.getCheckedItemPosition(); - long id = mAdapter.getItemId(position); - if (mCheckedId != id) { - // Clear the ListView's idea of the checked item, since - // it is incorrect - mListView.clearChoices(); - // Search for mCheckedId. If it is in the filtered list, - // mark it as checked - int count = mAdapter.getCount(); - for (int i = 0; i < count; i++) { - if (mAdapter.getItemId(i) == mCheckedId) { - mListView.setItemChecked(i, true); - break; - } - } - } - } - } - - @Override - public void run() { - if (mWebViewCore == null - || getWebView().getWindowToken() == null - || getWebView().getViewRootImpl() == null) { - // We've been detached and/or destroyed since this was posted - return; - } - final ListView listView = (ListView) LayoutInflater.from(mContext) - .inflate(com.android.internal.R.layout.select_dialog, null); - final MyArrayListAdapter adapter = new MyArrayListAdapter(); - AlertDialog.Builder b = new AlertDialog.Builder(mContext) - .setView(listView).setCancelable(true) - .setInverseBackgroundForced(true); - - if (mMultiple) { - b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mWebViewCore.sendMessage( - EventHub.LISTBOX_CHOICES, - adapter.getCount(), 0, - listView.getCheckedItemPositions()); - }}); - b.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mWebViewCore.sendMessage( - EventHub.SINGLE_LISTBOX_CHOICE, -2, 0); - }}); - } - mListBoxDialog = b.create(); - listView.setAdapter(adapter); - listView.setFocusableInTouchMode(true); - // There is a bug (1250103) where the checks in a ListView with - // multiple items selected are associated with the positions, not - // the ids, so the items do not properly retain their checks when - // filtered. Do not allow filtering on multiple lists until - // that bug is fixed. - - listView.setTextFilterEnabled(!mMultiple); - if (mMultiple) { - listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - int length = mSelectedArray.length; - for (int i = 0; i < length; i++) { - listView.setItemChecked(mSelectedArray[i], true); - } - } else { - listView.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View v, - int position, long id) { - // Rather than sending the message right away, send it - // after the page regains focus. - mListBoxMessage = Message.obtain(null, - EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0); - if (mListBoxDialog != null) { - mListBoxDialog.dismiss(); - mListBoxDialog = null; - } - } - }); - if (mSelection != -1) { - listView.setSelection(mSelection); - listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - listView.setItemChecked(mSelection, true); - DataSetObserver observer = new SingleDataSetObserver( - adapter.getItemId(mSelection), listView, adapter); - adapter.registerDataSetObserver(observer); - } - } - mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - if (mWebViewCore != null) { - mWebViewCore.sendMessage( - EventHub.SINGLE_LISTBOX_CHOICE, -2, 0); - } - mListBoxDialog = null; - } - }); - mListBoxDialog.show(); - } - } - - private Message mListBoxMessage; - - /* - * Request a dropdown menu for a listbox with multiple selection. - * - * @param array Labels for the listbox. - * @param enabledArray State for each element in the list. See static - * integers in Container class. - * @param selectedArray Which positions are initally selected. - */ - void requestListBox(String[] array, int[] enabledArray, int[] - selectedArray) { - mPrivateHandler.post( - new InvokeListBox(array, enabledArray, selectedArray)); - } - - /* - * Request a dropdown menu for a listbox with single selection or a single - * <select> element. - * - * @param array Labels for the listbox. - * @param enabledArray State for each element in the list. See static - * integers in Container class. - * @param selection Which position is initally selected. - */ - void requestListBox(String[] array, int[] enabledArray, int selection) { - mPrivateHandler.post( - new InvokeListBox(array, enabledArray, selection)); - } - - private int getScaledMaxXScroll() { - int width; - if (mHeightCanMeasure == false) { - width = getViewWidth() / 4; - } else { - Rect visRect = new Rect(); - calcOurVisibleRect(visRect); - width = visRect.width() / 2; - } - // FIXME the divisor should be retrieved from somewhere - return viewToContentX(width); - } - - private int getScaledMaxYScroll() { - int height; - if (mHeightCanMeasure == false) { - height = getViewHeight() / 4; - } else { - Rect visRect = new Rect(); - calcOurVisibleRect(visRect); - height = visRect.height() / 2; - } - // FIXME the divisor should be retrieved from somewhere - // the closest thing today is hard-coded into ScrollView.java - // (from ScrollView.java, line 363) int maxJump = height/2; - return Math.round(height * mZoomManager.getInvScale()); - } - - /** - * Called by JNI to invalidate view - */ - private void viewInvalidate() { - invalidate(); - } - - /** - * Pass the key directly to the page. This assumes that - * nativePageShouldHandleShiftAndArrows() returned true. - */ - private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) { - int keyEventAction; - if (down) { - keyEventAction = KeyEvent.ACTION_DOWN; - } else { - keyEventAction = KeyEvent.ACTION_UP; - } - - KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode, - 1, (metaState & KeyEvent.META_SHIFT_ON) - | (metaState & KeyEvent.META_ALT_ON) - | (metaState & KeyEvent.META_SYM_ON) - , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0); - sendKeyEvent(event); - } - - private void sendKeyEvent(KeyEvent event) { - int direction = 0; - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_DPAD_DOWN: - direction = View.FOCUS_DOWN; - break; - case KeyEvent.KEYCODE_DPAD_UP: - direction = View.FOCUS_UP; - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - direction = View.FOCUS_LEFT; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - direction = View.FOCUS_RIGHT; - break; - case KeyEvent.KEYCODE_TAB: - direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD; - break; - } - if (direction != 0 && mWebView.focusSearch(direction) == null) { - // Can't take focus in that direction - direction = 0; - } - int eventHubAction = EventHub.KEY_UP; - if (event.getAction() == KeyEvent.ACTION_DOWN) { - eventHubAction = EventHub.KEY_DOWN; - int sound = keyCodeToSoundsEffect(event.getKeyCode()); - if (sound != 0) { - mWebView.playSoundEffect(sound); - } - } - sendBatchableInputMessage(eventHubAction, direction, 0, event); - } - - /** - * See {@link WebView#setBackgroundColor(int)} - */ - @Override - public void setBackgroundColor(int color) { - mBackgroundColor = color; - mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color); - } - - /** - * Enable the communication b/t the webView and VideoViewProxy - * - * only used by the Browser - */ - public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) { - mHTML5VideoViewProxy = proxy; - } - - /** - * Set the time to wait between passing touches to WebCore. See also the - * TOUCH_SENT_INTERVAL member for further discussion. - * - * This is only used by the DRT test application. - */ - public void setTouchInterval(int interval) { - mCurrentTouchInterval = interval; - } - - /** - * Copy text into the clipboard. This is called indirectly from - * WebViewCore. - * @param text The text to put into the clipboard. - */ - private void copyToClipboard(String text) { - ClipboardManager cm = (ClipboardManager)mContext - .getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(getTitle(), text); - cm.setPrimaryClip(clip); - } - - /*package*/ void autoFillForm(int autoFillQueryId) { - mPrivateHandler.obtainMessage(AUTOFILL_FORM, autoFillQueryId, 0) - .sendToTarget(); - } - - /* package */ ViewManager getViewManager() { - return mViewManager; - } - - /** send content invalidate */ - protected void contentInvalidateAll() { - if (mWebViewCore != null && !mBlockWebkitViewMessages) { - mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL); - } - } - - /** discard all textures from tiles. Used in Profiled WebView */ - public void discardAllTextures() { - nativeDiscardAllTextures(); - } - - @Override - public void setLayerType(int layerType, Paint paint) { - updateHwAccelerated(); - } - - @Override - public void preDispatchDraw(Canvas canvas) { - // no-op for WebViewClassic. - } - - private void updateHwAccelerated() { - if (mNativeClass == 0) { - return; - } - boolean hwAccelerated = false; - if (mWebView.isHardwareAccelerated() - && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) { - hwAccelerated = true; - } - - // result is of type LayerAndroid::InvalidateFlags, non zero means invalidate/redraw - int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated); - if (mWebViewCore != null && !mBlockWebkitViewMessages && result != 0) { - mWebViewCore.contentDraw(); - } - } - - /** - * Begin collecting per-tile profiling data - * - * only used by profiling tests - */ - public void tileProfilingStart() { - nativeTileProfilingStart(); - } - /** - * Return per-tile profiling data - * - * only used by profiling tests - */ - public float tileProfilingStop() { - return nativeTileProfilingStop(); - } - - /** only used by profiling tests */ - public void tileProfilingClear() { - nativeTileProfilingClear(); - } - /** only used by profiling tests */ - public int tileProfilingNumFrames() { - return nativeTileProfilingNumFrames(); - } - /** only used by profiling tests */ - public int tileProfilingNumTilesInFrame(int frame) { - return nativeTileProfilingNumTilesInFrame(frame); - } - /** only used by profiling tests */ - public int tileProfilingGetInt(int frame, int tile, String key) { - return nativeTileProfilingGetInt(frame, tile, key); - } - /** only used by profiling tests */ - public float tileProfilingGetFloat(int frame, int tile, String key) { - return nativeTileProfilingGetFloat(frame, tile, key); - } - - /** - * Checks the focused content for an editable text field. This can be - * text input or ContentEditable. - * @return true if the focused item is an editable text field. - */ - boolean focusCandidateIsEditableText() { - if (mFocusedNode != null) { - return mFocusedNode.mEditable; - } - return false; - } - - // Called via JNI - private void postInvalidate() { - mWebView.postInvalidate(); - } - - // Note: must be called before first WebViewClassic is created. - public static void setShouldMonitorWebCoreThread() { - WebViewCore.setShouldMonitorWebCoreThread(); - } - - @Override - public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { - int layer = getBaseLayer(); - if (layer != 0) { - try { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - ViewStateSerializer.dumpLayerHierarchy(layer, stream, level); - stream.close(); - byte[] buf = stream.toByteArray(); - out.write(new String(buf, "ascii")); - } catch (IOException e) {} - } - } - - @Override - public View findHierarchyView(String className, int hashCode) { - if (mNativeClass == 0) return null; - Picture pic = new Picture(); - if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) { - return null; - } - return new PictureWrapperView(getContext(), pic, mWebView); - } - - private static class PictureWrapperView extends View { - Picture mPicture; - WebView mWebView; - - public PictureWrapperView(Context context, Picture picture, WebView parent) { - super(context); - mPicture = picture; - mWebView = parent; - setWillNotDraw(false); - setRight(mPicture.getWidth()); - setBottom(mPicture.getHeight()); - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawPicture(mPicture); - } - - @Override - public boolean post(Runnable action) { - return mWebView.post(action); - } - } - - private native void nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx); - private native void nativeDebugDump(); - private static native void nativeDestroy(int ptr); - - private native void nativeDraw(Canvas canvas, RectF visibleRect, - int color, int extra); - private native void nativeDumpDisplayTree(String urlOrNull); - private native boolean nativeEvaluateLayersAnimations(int nativeInstance); - private native int nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect, - Rect screenRect, RectF visibleContentRect, float scale, int extras); - private native int nativeGetDrawGLFunction(int nativeInstance); - private native void nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect, - Rect screenRect, RectF visibleContentRect, float scale); - private native String nativeGetSelection(); - private native void nativeSetHeightCanMeasure(boolean measure); - private native boolean nativeSetBaseLayer(int nativeInstance, - int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout, - int scrollingLayer); - private native int nativeGetBaseLayer(int nativeInstance); - private native void nativeCopyBaseContentToPicture(Picture pict); - private native boolean nativeDumpLayerContentToPicture(int nativeInstance, - String className, int layerId, Picture pict); - private native boolean nativeHasContent(); - private native void nativeStopGL(int ptr); - private native void nativeDiscardAllTextures(); - private native void nativeTileProfilingStart(); - private native float nativeTileProfilingStop(); - private native void nativeTileProfilingClear(); - private native int nativeTileProfilingNumFrames(); - private native int nativeTileProfilingNumTilesInFrame(int frame); - private native int nativeTileProfilingGetInt(int frame, int tile, String key); - private native float nativeTileProfilingGetFloat(int frame, int tile, String key); - - private native void nativeUseHardwareAccelSkia(boolean enabled); - - // Returns a pointer to the scrollable LayerAndroid at the given point. - private native int nativeScrollableLayer(int nativeInstance, int x, int y, Rect scrollRect, - Rect scrollBounds); - /** - * Scroll the specified layer. - * @param nativeInstance Native WebView instance - * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer. - * @param newX Destination x position to which to scroll. - * @param newY Destination y position to which to scroll. - * @return True if the layer is successfully scrolled. - */ - private native boolean nativeScrollLayer(int nativeInstance, int layer, int newX, int newY); - private native void nativeSetIsScrolling(boolean isScrolling); - private native int nativeGetBackgroundColor(int nativeInstance); - native boolean nativeSetProperty(String key, String value); - native String nativeGetProperty(String key); - /** - * See {@link ComponentCallbacks2} for the trim levels and descriptions - */ - private static native void nativeOnTrimMemory(int level); - private static native void nativeSetPauseDrawing(int instance, boolean pause); - private static native void nativeSetTextSelection(int instance, int selection); - private static native int nativeGetHandleLayerId(int instance, int handle, - Point cursorLocation, QuadF textQuad); - private static native void nativeMapLayerRect(int instance, int layerId, - Rect rect); - // Returns 1 if a layer sync is needed, else 0 - private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated); - private static native void nativeFindMaxVisibleRect(int instance, int layerId, - Rect visibleContentRect); - private static native boolean nativeIsHandleLeft(int instance, int handleId); - private static native boolean nativeIsPointVisible(int instance, - int layerId, int contentX, int contentY); -} diff --git a/core/java/android/webkit/WebViewClientClassicExt.java b/core/java/android/webkit/WebViewClientClassicExt.java deleted file mode 100644 index a873585..0000000 --- a/core/java/android/webkit/WebViewClientClassicExt.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.net.http.SslError; - -/** - * Adds WebViewClassic specific extension methods to the WebViewClient callback class. - * These are not part of the public WebView API, so the class is hidden. - * @hide - */ -public class WebViewClientClassicExt extends WebViewClient { - - /** - * Notify the host application that an SSL error occurred while loading a - * resource, but the WebView chose to proceed anyway based on a - * decision retained from a previous response to onReceivedSslError(). - */ - public void onProceededAfterSslError(WebView view, SslError error) { - } - - /** - * Notify the host application to handle a SSL client certificate - * request (display the request to the user and ask whether to - * proceed with a client certificate or not). The host application - * has to call either handler.cancel() or handler.proceed() as the - * connection is suspended and waiting for the response. The - * default behavior is to cancel, returning no client certificate. - * - * @param view The WebView that is initiating the callback. - * @param handler A ClientCertRequestHandler object that will - * handle the user's response. - * @param host_and_port The host and port of the requesting server. - */ - public void onReceivedClientCertRequest(WebView view, - ClientCertRequestHandler handler, String host_and_port) { - handler.cancel(); - } -} diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java deleted file mode 100644 index 4a09636..0000000 --- a/core/java/android/webkit/WebViewCore.java +++ /dev/null @@ -1,3145 +0,0 @@ -/* - * Copyright (C) 2007 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 android.webkit; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.graphics.Point; -import android.graphics.Rect; -import android.media.MediaFile; -import android.net.ProxyProperties; -import android.net.Uri; -import android.net.http.CertificateChainValidator; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.provider.MediaStore; -import android.util.Log; -import android.util.SparseBooleanArray; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.SurfaceView; -import android.view.View; -import android.webkit.WebViewClassic.FocusNodeHref; -import android.webkit.WebViewInputDispatcher.WebKitCallbacks; - -import com.android.internal.os.SomeArgs; - -import junit.framework.Assert; - -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; - -/** - * @hide - */ -public final class WebViewCore { - - private static final String LOGTAG = "webcore"; - - static { - // Load libwebcore and libchromium_net during static initialization. - // This happens in the zygote process so they will be shared read-only - // across all app processes. - try { - System.loadLibrary("webcore"); - System.loadLibrary("chromium_net"); - } catch (UnsatisfiedLinkError e) { - Log.e(LOGTAG, "Unable to load native support libraries."); - } - } - - /* - * WebViewCore always executes in the same thread as the native webkit. - */ - - // The WebViewClassic that corresponds to this WebViewCore. - private WebViewClassic mWebViewClassic; - // Proxy for handling callbacks from native code - private final CallbackProxy mCallbackProxy; - // Settings object for maintaining all settings - private final WebSettingsClassic mSettings; - // Context for initializing the BrowserFrame with the proper assets. - private final Context mContext; - // The pointer to a native view object. - private int mNativeClass; - // The BrowserFrame is an interface to the native Frame component. - private BrowserFrame mBrowserFrame; - // Custom JS interfaces to add during the initialization. - private Map<String, Object> mJavascriptInterfaces; - /* - * range is from 200 to 10,000. 0 is a special value means device-width. -1 - * means undefined. - */ - private int mViewportWidth = -1; - - /* - * range is from 200 to 10,000. 0 is a special value means device-height. -1 - * means undefined. - */ - private int mViewportHeight = -1; - - /* - * scale in percent, range is from 1 to 1000. 0 means undefined. - */ - private int mViewportInitialScale = 0; - - /* - * scale in percent, range is from 1 to 1000. 0 means undefined. - */ - private int mViewportMinimumScale = 0; - - /* - * scale in percent, range is from 1 to 1000. 0 means undefined. - */ - private int mViewportMaximumScale = 0; - - private boolean mViewportUserScalable = true; - - /* - * range is from 70 to 400. - * 0 is a special value means device-dpi. The default scale factor will be - * always 100. - * -1 means undefined. The default scale factor will be - * WebView.DEFAULT_SCALE_PERCENT. - */ - private int mViewportDensityDpi = -1; - - private boolean mIsRestored = false; - private float mRestoredScale = 0; - private float mRestoredTextWrapScale = 0; - private int mRestoredX = 0; - private int mRestoredY = 0; - - private MockGeolocation mMockGeolocation = new MockGeolocation(this); - - private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager = - new DeviceMotionAndOrientationManager(this); - private DeviceMotionService mDeviceMotionService; - private DeviceOrientationService mDeviceOrientationService; - - private int mLowMemoryUsageThresholdMb; - private int mHighMemoryUsageThresholdMb; - private int mHighUsageDeltaMb; - - private int mChromeCanFocusDirection; - private int mTextSelectionChangeReason = TextSelectionData.REASON_UNKNOWN; - - // Used to determine if we should monitor the WebCore thread for responsiveness. - // If it "hangs", for example a web page enters a while(true) loop, we will - // prompt the user with a dialog allowing them to terminate the process. - private static boolean sShouldMonitorWebCoreThread; - - // The thread name used to identify the WebCore thread and for use in - // debugging other classes that require operation within the WebCore thread. - /* package */ static final String THREAD_NAME = "WebViewCoreThread"; - - public WebViewCore(Context context, WebViewClassic w, CallbackProxy proxy, - Map<String, Object> javascriptInterfaces) { - // No need to assign this in the WebCore thread. - mCallbackProxy = proxy; - mWebViewClassic = w; - mJavascriptInterfaces = javascriptInterfaces; - // This context object is used to initialize the WebViewCore during - // subwindow creation. - mContext = context; - - // We need to wait for the initial thread creation before sending - // a message to the WebCore thread. - // XXX: This is the only time the UI thread will wait for the WebCore - // thread! - synchronized (WebViewCore.class) { - if (sWebCoreHandler == null) { - // Create a global thread and start it. - Thread t = new Thread(new WebCoreThread()); - t.setName(THREAD_NAME); - t.start(); - try { - WebViewCore.class.wait(); - } catch (InterruptedException e) { - Log.e(LOGTAG, "Caught exception while waiting for thread " + - "creation."); - Log.e(LOGTAG, Log.getStackTraceString(e)); - } - - if (sShouldMonitorWebCoreThread) { - // Start the singleton watchdog which will monitor the WebCore thread - // to verify it's still processing messages. Note that this is the only - // time we need to check the value as all the other public methods on - // the WebCoreThreadWatchdog are no-ops if start() is not called. - WebCoreThreadWatchdog.start(sWebCoreHandler); - } - } - // Make sure the Watchdog is aware of this new WebView. - WebCoreThreadWatchdog.registerWebView(w); - } - // Create an EventHub to handle messages before and after the thread is - // ready. - mEventHub = new EventHub(); - // Create a WebSettings object for maintaining all settings - mSettings = new WebSettingsClassic(mContext, mWebViewClassic); - // The WebIconDatabase needs to be initialized within the UI thread so - // just request the instance here. - WebIconDatabase.getInstance(); - // Create the WebStorageClassic singleton and the UI handler - WebStorageClassic.getInstance().createUIHandler(); - // Create the UI handler for GeolocationPermissions - GeolocationPermissionsClassic.getInstance().createUIHandler(); - - // Get the memory class of the current device. V8 will use these values - // to GC more effectively. - ActivityManager manager = (ActivityManager) mContext.getSystemService( - Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); - manager.getMemoryInfo(memInfo); - - // Allow us to use up to our memory class value before V8's GC kicks in. - // These values have been determined by experimentation. - mLowMemoryUsageThresholdMb = manager.getLargeMemoryClass(); - mHighMemoryUsageThresholdMb = (int) (mLowMemoryUsageThresholdMb * 1.5); - // Avoid constant V8 GC when memory usage equals to working set estimate. - mHighUsageDeltaMb = mLowMemoryUsageThresholdMb / 32; - - // Send a message to initialize the WebViewCore. - Message init = sWebCoreHandler.obtainMessage( - WebCoreThread.INITIALIZE, this); - sWebCoreHandler.sendMessage(init); - } - - /* Initialize private data within the WebCore thread. - */ - private void initialize() { - /* Initialize our private BrowserFrame class to handle all - * frame-related functions. We need to create a new view which - * in turn creates a C level FrameView and attaches it to the frame. - */ - mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy, - mSettings, mJavascriptInterfaces); - mJavascriptInterfaces = null; - // Sync the native settings and also create the WebCore thread handler. - mSettings.syncSettingsAndCreateHandler(mBrowserFrame); - // Create the handler and transfer messages for the IconDatabase - WebIconDatabaseClassic.getInstance().createHandler(); - // Create the handler for WebStorageClassic - WebStorageClassic.getInstance().createHandler(); - // Create the handler for GeolocationPermissions. - GeolocationPermissionsClassic.getInstance().createHandler(); - // The transferMessages call will transfer all pending messages to the - // WebCore thread handler. - mEventHub.transferMessages(); - - // Send a message back to WebView to tell it that we have set up the - // WebCore thread. - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.WEBCORE_INITIALIZED_MSG_ID, - mNativeClass, 0).sendToTarget(); - } - - } - - /* Handle the initialization of WebViewCore during subwindow creation. This - * method is called from the WebCore thread but it is called before the - * INITIALIZE message can be handled. - */ - /* package */ void initializeSubwindow() { - // Go ahead and initialize the core components. - initialize(); - // Remove the INITIALIZE method so we don't try to initialize twice. - sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this); - } - - /* Get the BrowserFrame component. This is used for subwindow creation and - * is called only from BrowserFrame in the WebCore thread. */ - /* package */ synchronized BrowserFrame getBrowserFrame() { - return mBrowserFrame; - } - - public WebKitCallbacks getInputDispatcherCallbacks() { - return mEventHub; - } - - //------------------------------------------------------------------------- - // Common methods - //------------------------------------------------------------------------- - - /** - * Causes all timers to pause. This applies to all WebViews in the current - * app process. - */ - public static void pauseTimers() { - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException( - "No WebView has been created in this process!"); - } - BrowserFrame.sJavaBridge.pause(); - } - - /** - * Resume all timers. This applies to all WebViews in the current process. - */ - public static void resumeTimers() { - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException( - "No WebView has been created in this process!"); - } - BrowserFrame.sJavaBridge.resume(); - } - - public WebSettingsClassic getSettings() { - return mSettings; - } - - /* - * Given mimeType, check whether it's supported in Android media framework. - * mimeType could be such as "audio/ogg" and "video/mp4". - */ - /* package */ static boolean isSupportedMediaMimeType(String mimeType) { - int fileType = MediaFile.getFileTypeForMimeType(mimeType); - return MediaFile.isAudioFileType(fileType) - || MediaFile.isVideoFileType(fileType) - || MediaFile.isPlayListFileType(fileType) - // The following is not in Media framework, but it's supported. - || (mimeType != null && mimeType.startsWith("video/m4v")); - } - - /** - * Add an error message to the client's console. - * @param message The message to add - * @param lineNumber the line on which the error occurred - * @param sourceID the filename of the source that caused the error. - * @param msgLevel the log level of this message. This is a value casted to int - * from WebCore::MessageLevel in WebCore/page/Console.h. - */ - protected void addMessageToConsole(String message, int lineNumber, String sourceID, - int msgLevel) { - mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel); - } - - /** - * Invoke a javascript alert. - * @param message The message displayed in the alert. - */ - protected void jsAlert(String url, String message) { - mCallbackProxy.onJsAlert(url, message); - } - - /** - * Called by JNI when the focus node changed. - */ - private void focusNodeChanged(int nodePointer, WebKitHitTest hitTest) { - if (mWebViewClassic == null) return; - mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.FOCUS_NODE_CHANGED, - nodePointer, 0, hitTest).sendToTarget(); - } - - /** - * Called by JNI to advance focus to the next view. - */ - private void chromeTakeFocus(int webkitDirection) { - if (mWebViewClassic == null) return; - Message m = mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.TAKE_FOCUS); - m.arg1 = mapDirection(webkitDirection); - m.sendToTarget(); - } - - /** - * Called by JNI to see if we can take focus in the given direction. - */ - private boolean chromeCanTakeFocus(int webkitDirection) { - int direction = mapDirection(webkitDirection); - return direction == mChromeCanFocusDirection && direction != 0; - } - - /** - * Maps a Webkit focus direction to a framework one - */ - private int mapDirection(int webkitDirection) { - /* - * This is WebKit's FocusDirection enum (from FocusDirection.h) - enum FocusDirection { - FocusDirectionNone = 0, - FocusDirectionForward, - FocusDirectionBackward, - FocusDirectionUp, - FocusDirectionDown, - FocusDirectionLeft, - FocusDirectionRight - }; - */ - switch (webkitDirection) { - case 1: - return View.FOCUS_FORWARD; - case 2: - return View.FOCUS_BACKWARD; - case 3: - return View.FOCUS_UP; - case 4: - return View.FOCUS_DOWN; - case 5: - return View.FOCUS_LEFT; - case 6: - return View.FOCUS_RIGHT; - } - return 0; - } - - /** - * Called by JNI. Open a file chooser to upload a file. - * @param acceptType The value of the 'accept' attribute of the - * input tag associated with this file picker. - * @param capture The value of the 'capture' attribute of the - * input tag associated with this file picker. - * @return String version of the URI. - */ - private String openFileChooser(String acceptType, String capture) { - Uri uri = mCallbackProxy.openFileChooser(acceptType, capture); - if (uri != null) { - String filePath = ""; - // Note - querying for MediaStore.Images.Media.DATA - // seems to work for all content URIs, not just images - Cursor cursor = mContext.getContentResolver().query( - uri, - new String[] { MediaStore.Images.Media.DATA }, - null, null, null); - if (cursor != null) { - try { - if (cursor.moveToNext()) { - filePath = cursor.getString(0); - } - } finally { - cursor.close(); - } - } else { - filePath = uri.getLastPathSegment(); - } - String uriString = uri.toString(); - BrowserFrame.sJavaBridge.storeFilePathForContentUri(filePath, uriString); - return uriString; - } - return ""; - } - - /** - * Notify the embedding application that the origin has exceeded it's database quota. - * @param url The URL that caused the overflow. - * @param databaseIdentifier The identifier of the database. - * @param quota The current quota for the origin. - * @param estimatedDatabaseSize The estimated size of the database. - */ - protected void exceededDatabaseQuota(String url, - String databaseIdentifier, - long quota, - long estimatedDatabaseSize) { - // Inform the callback proxy of the quota overflow. Send an object - // that encapsulates a call to the nativeSetDatabaseQuota method to - // awaken the sleeping webcore thread when a decision from the - // client to allow or deny quota is available. - mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier, - quota, estimatedDatabaseSize, getUsedQuota(), - new WebStorage.QuotaUpdater() { - @Override - public void updateQuota(long newQuota) { - nativeSetNewStorageLimit(mNativeClass, newQuota); - } - }); - } - - /** - * Notify the embedding application that the appcache has reached or exceeded its maximum - * allowed storage size. - * - * @param requiredStorage is the amount of storage, in bytes, that would be - * needed in order for the last appcache operation to succeed. - * @param maxSize maximum allowed Application Cache database size, in bytes. - */ - protected void reachedMaxAppCacheSize(long requiredStorage, long maxSize) { - mCallbackProxy.onReachedMaxAppCacheSize(requiredStorage, maxSize, - new WebStorage.QuotaUpdater() { - @Override - public void updateQuota(long newQuota) { - nativeSetNewStorageLimit(mNativeClass, newQuota); - } - }); - } - - protected void populateVisitedLinks() { - ValueCallback callback = new ValueCallback<String[]>() { - @Override - public void onReceiveValue(String[] value) { - sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value); - } - }; - mCallbackProxy.getVisitedHistory(callback); - } - - /** - * Shows a prompt to ask the user to set the Geolocation permission state - * for the given origin. - * @param origin The origin for which Geolocation permissions are - * requested. - */ - protected void geolocationPermissionsShowPrompt(String origin) { - mCallbackProxy.onGeolocationPermissionsShowPrompt(origin, - new GeolocationPermissions.Callback() { - @Override - public void invoke(String origin, boolean allow, boolean remember) { - GeolocationPermissionsData data = new GeolocationPermissionsData(); - data.mOrigin = origin; - data.mAllow = allow; - data.mRemember = remember; - // Marshall to WebCore thread. - sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data); - } - }); - } - - /** - * Hides the Geolocation permissions prompt. - */ - protected void geolocationPermissionsHidePrompt() { - mCallbackProxy.onGeolocationPermissionsHidePrompt(); - } - - /** - * Invoke a javascript confirm dialog. - * @param message The message displayed in the dialog. - * @return True if the user confirmed or false if the user cancelled. - */ - protected boolean jsConfirm(String url, String message) { - return mCallbackProxy.onJsConfirm(url, message); - } - - /** - * Invoke a javascript prompt dialog. - * @param message The message to be displayed in the dialog. - * @param defaultValue The default value in the prompt input. - * @return The input from the user or null to indicate the user cancelled - * the dialog. - */ - protected String jsPrompt(String url, String message, String defaultValue) { - return mCallbackProxy.onJsPrompt(url, message, defaultValue); - } - - /** - * Invoke a javascript before unload dialog. - * @param url The url that is requesting the dialog. - * @param message The message displayed in the dialog. - * @return True if the user confirmed or false if the user cancelled. False - * will cancel the navigation. - */ - protected boolean jsUnload(String url, String message) { - return mCallbackProxy.onJsBeforeUnload(url, message); - } - - /** - * - * Callback to notify that a JavaScript execution timeout has occured. - * @return True if the JavaScript execution should be interrupted. False - * will continue the execution. - */ - protected boolean jsInterrupt() { - return mCallbackProxy.onJsTimeout(); - } - - /** - * Notify the webview that we want to exit the video fullscreen. - * This is called through JNI by webcore. - */ - protected void exitFullscreenVideo() { - if (mWebViewClassic == null) return; - Message message = Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.EXIT_FULLSCREEN_VIDEO); - message.sendToTarget(); - } - - /** - * Clear the picture set. To be called only on the WebCore thread. - */ - /* package */ void clearContent() { - nativeClearContent(mNativeClass); - } - - //------------------------------------------------------------------------- - // JNI methods - //------------------------------------------------------------------------- - - static native String nativeFindAddress(String addr, boolean caseInsensitive); - - /** - * Empty the picture set. - */ - private native void nativeClearContent(int nativeClass); - - private native void nativeContentInvalidateAll(int nativeClass); - - /** - * Redraw a portion of the picture set. The Point wh returns the - * width and height of the overall picture. - */ - private native int nativeRecordContent(int nativeClass, Point wh); - - /** - * Notify webkit that animations have begun (on the hardware accelerated content) - */ - private native void nativeNotifyAnimationStarted(int nativeClass); - - private native boolean nativeKey(int nativeClass, int keyCode, - int unichar, int repeatCount, boolean isShift, boolean isAlt, - boolean isSym, boolean isDown); - - private native void nativeSendListBoxChoices(int nativeClass, - boolean[] choices, int size); - - private native void nativeSendListBoxChoice(int nativeClass, int choice); - - private native void nativeCloseIdleConnections(int nativeClass); - - /* Tell webkit what its width and height are, for the purposes - of layout/line-breaking. These coordinates are in document space, - which is the same as View coords unless we have zoomed the document - (see nativeSetZoom). - textWrapWidth is used by layout to wrap column around. If viewport uses - fixed size, textWrapWidth can be different from width with zooming. - should this be called nativeSetViewPortSize? - */ - private native void nativeSetSize(int nativeClass, int width, int height, - int textWrapWidth, float scale, int screenWidth, int screenHeight, - int anchorX, int anchorY, boolean ignoreHeight); - - private native int nativeGetContentMinPrefWidth(int nativeClass); - - // Start: functions that deal with text editing - private native void nativeReplaceTextfieldText( - int nativeClass, int oldStart, int oldEnd, String replace, - int newStart, int newEnd, int textGeneration); - - private native void passToJs(int nativeClass, - int gen, String currentText, int keyCode, int keyValue, - boolean down, boolean cap, boolean fn, boolean sym); - - private native void nativeSetFocusControllerActive(int nativeClass, - boolean active); - - private native void nativeSaveDocumentState(int nativeClass); - - private native void nativeMoveMouse(int nativeClass, int x, int y); - - private native String nativeRetrieveHref(int nativeClass, int x, int y); - private native String nativeRetrieveAnchorText(int nativeClass, - int x, int y); - private native String nativeRetrieveImageSource(int nativeClass, - int x, int y); - private native boolean nativeMouseClick(int nativeClass); - - private native int nativeHandleTouchEvent(int nativeClass, int action, - int[] idArray, int[] xArray, int[] yArray, int count, - int actionIndex, int metaState); - - private native void nativeSetBackgroundColor(int nativeClass, int color); - - private native void nativeDumpDomTree(int nativeClass, boolean useFile); - - private native void nativeDumpRenderTree(int nativeClass, boolean useFile); - - private native void nativeSetJsFlags(int nativeClass, String flags); - - /** - * Delete text from start to end in the focused textfield. If there is no - * focus, or if start == end, silently fail. If start and end are out of - * order, swap them. - * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass - * @param start Beginning of selection to delete. - * @param end End of selection to delete. - * @param textGeneration Text generation number when delete was pressed. - */ - private native void nativeDeleteSelection(int nativeClass, int start, - int end, int textGeneration); - - /** - * Set the selection to (start, end) in the focused textfield. If start and - * end are out of order, swap them. - * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass - * @param start Beginning of selection. - * @param end End of selection. - */ - private native void nativeSetSelection(int nativeClass, int start, int end); - - // Register a scheme to be treated as local scheme so that it can access - // local asset files for resources - private native void nativeRegisterURLSchemeAsLocal(int nativeClass, - String scheme); - - /* - * Inform webcore that the user has decided whether to allow or deny new - * quota for the current origin or more space for the app cache, and that - * the main thread should wake up now. - * @param limit Is the new quota for an origin or new app cache max size. - */ - private native void nativeSetNewStorageLimit(int nativeClass, long limit); - - /** - * Provide WebCore with a Geolocation permission state for the specified - * origin. - * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass - * @param origin The origin for which Geolocation permissions are provided. - * @param allow Whether Geolocation permissions are allowed. - * @param remember Whether this decision should be remembered beyond the - * life of the current page. - */ - private native void nativeGeolocationPermissionsProvide(int nativeClass, - String origin, boolean allow, boolean remember); - - /** - * Provide WebCore with the previously visted links from the history database - * @param nativeClass TODO - */ - private native void nativeProvideVisitedHistory(int nativeClass, - String[] history); - - /** - * Modifies the current selection. - * - * Note: Accessibility support. - * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass - * @param direction The direction in which to alter the selection. - * @param granularity The granularity of the selection modification. - * - * @return The selection string. - */ - private native String nativeModifySelection(int nativeClass, int direction, - int granularity); - - // EventHub for processing messages - private final EventHub mEventHub; - // WebCore thread handler - private static Handler sWebCoreHandler; - // Class for providing Handler creation inside the WebCore thread. - private static class WebCoreThread implements Runnable { - // Message id for initializing a new WebViewCore. - private static final int INITIALIZE = 0; - private static final int REDUCE_PRIORITY = 1; - private static final int RESUME_PRIORITY = 2; - - @Override - public void run() { - Looper.prepare(); - Assert.assertNull(sWebCoreHandler); - synchronized (WebViewCore.class) { - sWebCoreHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case INITIALIZE: - WebViewCore core = (WebViewCore) msg.obj; - core.initialize(); - break; - - case REDUCE_PRIORITY: - // 3 is an adjustable number. - Process.setThreadPriority( - Process.THREAD_PRIORITY_DEFAULT + 3 * - Process.THREAD_PRIORITY_LESS_FAVORABLE); - break; - - case RESUME_PRIORITY: - Process.setThreadPriority( - Process.THREAD_PRIORITY_DEFAULT); - break; - - case EventHub.ADD_PACKAGE_NAME: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException( - "No WebView has been created in this process!"); - } - BrowserFrame.sJavaBridge.addPackageName((String) msg.obj); - break; - - case EventHub.REMOVE_PACKAGE_NAME: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException( - "No WebView has been created in this process!"); - } - BrowserFrame.sJavaBridge.removePackageName((String) msg.obj); - break; - - case EventHub.PROXY_CHANGED: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException( - "No WebView has been created in this process!"); - } - BrowserFrame.sJavaBridge.updateProxy((ProxyProperties)msg.obj); - break; - - case EventHub.HEARTBEAT: - // Ping back the watchdog to let it know we're still processing - // messages. - Message m = (Message)msg.obj; - m.sendToTarget(); - break; - case EventHub.TRUST_STORAGE_UPDATED: - // post a task to network thread for updating trust manager - nativeCertTrustChanged(); - CertificateChainValidator.handleTrustStorageUpdate(); - break; - } - } - }; - WebViewCore.class.notify(); - } - Looper.loop(); - } - } - - static class BaseUrlData { - String mBaseUrl; - String mData; - String mMimeType; - String mEncoding; - String mHistoryUrl; - } - - static class JSInterfaceData { - Object mObject; - String mInterfaceName; - boolean mRequireAnnotation; - } - - static class JSKeyData { - String mCurrentText; - KeyEvent mEvent; - } - - static class MotionUpData { - int mFrame; - int mNode; - Rect mBounds; - int mX; - int mY; - } - - static class GetUrlData { - String mUrl; - Map<String, String> mExtraHeaders; - } - - static class PostUrlData { - String mUrl; - byte[] mPostData; - } - - static class ReplaceTextData { - String mReplace; - int mNewStart; - int mNewEnd; - int mTextGeneration; - } - - static class TextSelectionData { - static final int REASON_UNKNOWN = 0; - static final int REASON_ACCESSIBILITY_INJECTOR = 1; - static final int REASON_SELECT_WORD = 2; - public TextSelectionData(int start, int end, int selectTextPtr) { - mStart = start; - mEnd = end; - mSelectTextPtr = selectTextPtr; - } - int mStart; - int mEnd; - int mSelectTextPtr; - int mSelectionReason = TextSelectionData.REASON_UNKNOWN; - } - - static class TouchUpData { - int mMoveGeneration; - int mFrame; - int mNode; - int mX; - int mY; - int mNativeLayer; - Rect mNativeLayerRect = new Rect(); - } - - static class TouchHighlightData { - int mX; - int mY; - int mSlop; - int mNativeLayer; - Rect mNativeLayerRect; - } - - static class WebKitHitTest { - String mLinkUrl; - String mIntentUrl; - String mAnchorText; - String mImageUrl; - String mAltDisplayString; - String mTitle; - Rect[] mTouchRects; - boolean mEditable; - int mTapHighlightColor = WebViewClassic.HIGHLIGHT_COLOR; - Rect[] mEnclosingParentRects; - boolean mHasFocus; - - // These are the input values that produced this hit test - int mHitTestX; - int mHitTestY; - int mHitTestSlop; - boolean mHitTestMovedMouse; - } - - static class AutoFillData { - public AutoFillData() { - mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE; - mPreview = ""; - } - - public AutoFillData(int queryId, String preview) { - mQueryId = queryId; - mPreview = preview; - } - - public int getQueryId() { - return mQueryId; - } - - public String getPreviewString() { - return mPreview; - } - - private int mQueryId; - private String mPreview; - } - - static class TextFieldInitData { - public int mFieldPointer; - public String mText; - public int mType; - public boolean mIsSpellCheckEnabled; - public boolean mIsTextFieldNext; - public boolean mIsTextFieldPrev; - public boolean mIsAutoCompleteEnabled; - public String mName; - public String mLabel; - public int mMaxLength; - public Rect mContentBounds; - public int mNodeLayerId; - public Rect mClientRect; - } - - // mAction of TouchEventData can be MotionEvent.getAction() which uses the - // last two bytes or one of the following values - static final int ACTION_LONGPRESS = 0x100; - static final int ACTION_DOUBLETAP = 0x200; - - private static final int TOUCH_FLAG_HIT_HANDLER = 0x1; - private static final int TOUCH_FLAG_PREVENT_DEFAULT = 0x2; - - static class TouchEventData { - int mAction; - int[] mIds; // Ids of the touch points - Point[] mPoints; - Point[] mPointsInView; // the point coordinates in view axis. - int mActionIndex; // Associated pointer index for ACTION_POINTER_DOWN/UP - int mMetaState; - boolean mReprocess; - MotionEvent mMotionEvent; - int mNativeLayer; - Rect mNativeLayerRect = new Rect(); - long mSequence; - boolean mNativeResult; - } - - static class GeolocationPermissionsData { - String mOrigin; - boolean mAllow; - boolean mRemember; - } - - static final String[] HandlerDebugString = { - "REVEAL_SELECTION", // 96 - "", // 97 - "", // = 98 - "SCROLL_TEXT_INPUT", // = 99 - "LOAD_URL", // = 100; - "STOP_LOADING", // = 101; - "RELOAD", // = 102; - "KEY_DOWN", // = 103; - "KEY_UP", // = 104; - "VIEW_SIZE_CHANGED", // = 105; - "GO_BACK_FORWARD", // = 106; - "SET_SCROLL_OFFSET", // = 107; - "RESTORE_STATE", // = 108; - "PAUSE_TIMERS", // = 109; - "RESUME_TIMERS", // = 110; - "CLEAR_CACHE", // = 111; - "CLEAR_HISTORY", // = 112; - "SET_SELECTION", // = 113; - "REPLACE_TEXT", // = 114; - "PASS_TO_JS", // = 115; - "SET_GLOBAL_BOUNDS", // = 116; - "", // = 117; - "CLICK", // = 118; - "SET_NETWORK_STATE", // = 119; - "DOC_HAS_IMAGES", // = 120; - "FAKE_CLICK", // = 121; - "DELETE_SELECTION", // = 122; - "LISTBOX_CHOICES", // = 123; - "SINGLE_LISTBOX_CHOICE", // = 124; - "MESSAGE_RELAY", // = 125; - "SET_BACKGROUND_COLOR", // = 126; - "SET_MOVE_FOCUS", // = 127 - "SAVE_DOCUMENT_STATE", // = 128; - "129", // = 129; - "WEBKIT_DRAW", // = 130; - "131", // = 131; - "POST_URL", // = 132; - "", // = 133; - "CLEAR_CONTENT", // = 134; - "", // = 135; - "", // = 136; - "REQUEST_CURSOR_HREF", // = 137; - "ADD_JS_INTERFACE", // = 138; - "LOAD_DATA", // = 139; - "", // = 140; - "", // = 141; - "SET_ACTIVE", // = 142; - "ON_PAUSE", // = 143 - "ON_RESUME", // = 144 - "FREE_MEMORY", // = 145 - "VALID_NODE_BOUNDS", // = 146 - "SAVE_WEBARCHIVE", // = 147 - "WEBKIT_DRAW_LAYERS", // = 148; - "REMOVE_JS_INTERFACE", // = 149; - }; - - static class FindAllRequest { - public FindAllRequest(String text) { - mSearchText = text; - mMatchCount = -1; - mMatchIndex = -1; - } - public final String mSearchText; - public int mMatchCount; - public int mMatchIndex; - } - - static class SaveViewStateRequest { - SaveViewStateRequest(OutputStream s, ValueCallback<Boolean> cb) { - mStream = s; - mCallback = cb; - } - public OutputStream mStream; - public ValueCallback<Boolean> mCallback; - } - - /** - * @hide - */ - public class EventHub implements WebViewInputDispatcher.WebKitCallbacks { - // Message Ids - static final int REVEAL_SELECTION = 96; - static final int SCROLL_TEXT_INPUT = 99; - static final int LOAD_URL = 100; - static final int STOP_LOADING = 101; - static final int RELOAD = 102; - static final int KEY_DOWN = 103; - static final int KEY_UP = 104; - static final int VIEW_SIZE_CHANGED = 105; - static final int GO_BACK_FORWARD = 106; - static final int SET_SCROLL_OFFSET = 107; - static final int RESTORE_STATE = 108; - static final int PAUSE_TIMERS = 109; - static final int RESUME_TIMERS = 110; - static final int CLEAR_CACHE = 111; - static final int CLEAR_HISTORY = 112; - static final int SET_SELECTION = 113; - static final int REPLACE_TEXT = 114; - static final int PASS_TO_JS = 115; - static final int SET_GLOBAL_BOUNDS = 116; - static final int SET_NETWORK_STATE = 119; - static final int DOC_HAS_IMAGES = 120; - static final int DELETE_SELECTION = 122; - static final int LISTBOX_CHOICES = 123; - static final int SINGLE_LISTBOX_CHOICE = 124; - public static final int MESSAGE_RELAY = 125; - static final int SET_BACKGROUND_COLOR = 126; - static final int SAVE_DOCUMENT_STATE = 128; - static final int DELETE_SURROUNDING_TEXT = 129; - - - static final int WEBKIT_DRAW = 130; - static final int POST_URL = 132; - static final int CLEAR_CONTENT = 134; - - // UI nav messages - static final int SET_MOVE_MOUSE = 135; - static final int REQUEST_CURSOR_HREF = 137; - static final int ADD_JS_INTERFACE = 138; - static final int LOAD_DATA = 139; - - // Used to tell the focus controller not to draw the blinking cursor, - // based on whether the WebView has focus and whether the WebView's - // cursor matches the webpage's focus. - static final int SET_ACTIVE = 142; - - // lifecycle activities for just this DOM (unlike pauseTimers, which - // is global) - static final int ON_PAUSE = 143; - static final int ON_RESUME = 144; - static final int FREE_MEMORY = 145; - - // Load and save web archives - static final int SAVE_WEBARCHIVE = 147; - - static final int REMOVE_JS_INTERFACE = 149; - - // Network-based messaging - static final int CLEAR_SSL_PREF_TABLE = 150; - - // Test harness messages - static final int REQUEST_EXT_REPRESENTATION = 160; - static final int REQUEST_DOC_AS_TEXT = 161; - - // debugging - static final int DUMP_DOMTREE = 170; - static final int DUMP_RENDERTREE = 171; - - static final int SET_JS_FLAGS = 174; - static final int CONTENT_INVALIDATE_ALL = 175; - // Geolocation - static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180; - - static final int POPULATE_VISITED_LINKS = 181; - - static final int HIDE_FULLSCREEN = 182; - - static final int SET_NETWORK_TYPE = 183; - - // navigator.isApplicationInstalled() - static final int ADD_PACKAGE_NAMES = 184; - static final int ADD_PACKAGE_NAME = 185; - static final int REMOVE_PACKAGE_NAME = 186; - - // accessibility support - static final int MODIFY_SELECTION = 190; - - static final int SET_USE_MOCK_DEVICE_ORIENTATION = 191; - - static final int AUTOFILL_FORM = 192; - - static final int PROXY_CHANGED = 193; - - static final int EXECUTE_JS = 194; - - static final int PLUGIN_SURFACE_READY = 195; - - static final int NOTIFY_ANIMATION_STARTED = 196; - - static final int HEARTBEAT = 197; - - static final int SCROLL_LAYER = 198; - - // private message ids - private static final int DESTROY = 200; - - // for cut & paste - static final int COPY_TEXT = 210; - static final int DELETE_TEXT = 211; - static final int INSERT_TEXT = 212; - static final int SELECT_TEXT = 213; - static final int SELECT_WORD_AT = 214; - static final int SELECT_ALL = 215; - - // for updating state on trust storage change - static final int TRUST_STORAGE_UPDATED = 220; - - // find-on-page controls - static final int FIND_ALL = 221; - static final int FIND_NEXT = 222; - - // key was pressed (down and up) - static final int KEY_PRESS = 223; - static final int SET_INITIAL_FOCUS = 224; - - static final int SAVE_VIEW_STATE = 225; - static final int SET_USE_MOCK_GEOLOCATION = 226; - - // Private handler for WebCore messages. - private Handler mHandler; - // Message queue for containing messages before the WebCore thread is - // ready. - private LinkedList<Message> mMessages = new LinkedList<Message>(); - // Flag for blocking messages. This is used during DESTROY to avoid - // posting more messages to the EventHub or to WebView's event handler. - private boolean mBlockMessages; - private boolean mDestroying; - - private int mTid; - private int mSavedPriority; - - /** - * Prevent other classes from creating an EventHub. - */ - private EventHub() {} - - private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION; - private static final int LAST_PACKAGE_MSG_ID = REMOVE_JS_INTERFACE; - - /** - * Transfer all messages to the newly created webcore thread handler. - */ - private void transferMessages() { - mTid = Process.myTid(); - mSavedPriority = Process.getThreadPriority(mTid); - - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID - || msg.what > LAST_PACKAGE_MSG_ID - ? Integer.toString(msg.what) - : HandlerDebugString[msg.what - - FIRST_PACKAGE_MSG_ID]) - + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 - + " obj=" + msg.obj); - } - switch (msg.what) { - case PAUSE_TIMERS: - mSavedPriority = Process.getThreadPriority(mTid); - Process.setThreadPriority(mTid, - Process.THREAD_PRIORITY_BACKGROUND); - pauseTimers(); - if (mNativeClass != 0) { - nativeCloseIdleConnections(mNativeClass); - } - return; - - case RESUME_TIMERS: - Process.setThreadPriority(mTid, mSavedPriority); - resumeTimers(); - return; - } - - if (mWebViewClassic == null || mNativeClass == 0) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.w(LOGTAG, "Rejecting message " + msg.what - + " because we are destroyed"); - } - return; - } - if (mDestroying == true - && msg.what != EventHub.DESTROY) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "Rejecting message " + msg.what - + " because we are being destroyed"); - } - return; - } - switch (msg.what) { - case WEBKIT_DRAW: - webkitDraw(); - break; - - case DESTROY: - // Time to take down the world. Cancel all pending - // loads and destroy the native view and frame. - synchronized (WebViewCore.this) { - mCallbackProxy.shutdown(); - // Wake up the WebCore thread just in case it is waiting for a - // JavaScript dialog. - synchronized (mCallbackProxy) { - mCallbackProxy.notify(); - } - mBrowserFrame.destroy(); - mBrowserFrame = null; - mSettings.onDestroyed(); - mNativeClass = 0; - WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic); - mWebViewClassic = null; - } - break; - - case REVEAL_SELECTION: - nativeRevealSelection(mNativeClass); - break; - - case SCROLL_TEXT_INPUT: - float xPercent; - if (msg.obj == null) { - xPercent = 0f; - } else { - xPercent = ((Float) msg.obj).floatValue(); - } - nativeScrollFocusedTextInput(mNativeClass, xPercent, - msg.arg2); - break; - - case LOAD_URL: { - CookieManagerClassic.getInstance().waitForCookieOperationsToComplete(); - GetUrlData param = (GetUrlData) msg.obj; - loadUrl(param.mUrl, param.mExtraHeaders); - break; - } - - case POST_URL: { - CookieManagerClassic.getInstance().waitForCookieOperationsToComplete(); - PostUrlData param = (PostUrlData) msg.obj; - mBrowserFrame.postUrl(param.mUrl, param.mPostData); - break; - } - case LOAD_DATA: - CookieManagerClassic.getInstance().waitForCookieOperationsToComplete(); - BaseUrlData loadParams = (BaseUrlData) msg.obj; - String baseUrl = loadParams.mBaseUrl; - if (baseUrl != null) { - int i = baseUrl.indexOf(':'); - if (i > 0) { - // In 1.0, WebView.loadDataWithBaseURL() could access local - // asset files using 'file' scheme URLs as long as the data is - // valid. Later versions of WebKit have tightened the - // restriction around when pages can access such local URLs. - // To maintain compatibility with 1.0, we register the scheme of - // the baseUrl to be considered local, as long as it is not - // http(s)/ftp(s)/about/javascript. - String scheme = baseUrl.substring(0, i); - if (!scheme.startsWith("http") && - !scheme.startsWith("ftp") && - !scheme.startsWith("about") && - !scheme.startsWith("javascript")) { - nativeRegisterURLSchemeAsLocal(mNativeClass, - scheme); - } - } - } - mBrowserFrame.loadData(baseUrl, - loadParams.mData, - loadParams.mMimeType, - loadParams.mEncoding, - loadParams.mHistoryUrl); - nativeContentInvalidateAll(mNativeClass); - break; - - case STOP_LOADING: - // If the WebCore has committed the load, but not - // finished the first layout yet, we need to set - // first layout done to trigger the interpreted side sync - // up with native side - if (mBrowserFrame.committed() - && !mBrowserFrame.firstLayoutDone()) { - mBrowserFrame.didFirstLayout(); - } - // Do this after syncing up the layout state. - stopLoading(); - break; - - case RELOAD: - mBrowserFrame.reload(false); - break; - - case KEY_DOWN: - key((KeyEvent) msg.obj, msg.arg1, true); - break; - - case KEY_UP: - key((KeyEvent) msg.obj, msg.arg1, false); - break; - - case KEY_PRESS: - keyPress(msg.arg1); - break; - - case VIEW_SIZE_CHANGED: { - viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj); - break; - } - case SET_SCROLL_OFFSET: - // note: these are in document coordinates - // (inv-zoom) - Point pt = (Point) msg.obj; - nativeSetScrollOffset(mNativeClass, - msg.arg1 == 1, pt.x, pt.y); - break; - - case SET_GLOBAL_BOUNDS: - Rect r = (Rect) msg.obj; - nativeSetGlobalBounds(mNativeClass, r.left, r.top, - r.width(), r.height()); - break; - - case GO_BACK_FORWARD: - // If it is a standard load and the load is not - // committed yet, we interpret BACK as RELOAD - if (!mBrowserFrame.committed() && msg.arg1 == -1 && - (mBrowserFrame.loadType() == - BrowserFrame.FRAME_LOADTYPE_STANDARD)) { - mBrowserFrame.reload(true); - } else { - mBrowserFrame.goBackOrForward(msg.arg1); - } - break; - - case RESTORE_STATE: - stopLoading(); - restoreState(msg.arg1); - break; - - - case ON_PAUSE: - nativePause(mNativeClass); - break; - - case ON_RESUME: - nativeResume(mNativeClass); - break; - - case FREE_MEMORY: - clearCache(false); - nativeFreeMemory(mNativeClass); - break; - - case SET_NETWORK_STATE: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException("No WebView " + - "has been created in this process!"); - } - BrowserFrame.sJavaBridge - .setNetworkOnLine(msg.arg1 == 1); - break; - - case SET_NETWORK_TYPE: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException("No WebView " + - "has been created in this process!"); - } - Map<String, String> map = (Map<String, String>) msg.obj; - BrowserFrame.sJavaBridge - .setNetworkType(map.get("type"), map.get("subtype")); - break; - - case CLEAR_CACHE: - clearCache(msg.arg1 == 1); - break; - - case CLEAR_HISTORY: - mCallbackProxy.getBackForwardList(). - close(mBrowserFrame.mNativeFrame); - break; - - case REPLACE_TEXT: - ReplaceTextData rep = (ReplaceTextData) msg.obj; - nativeReplaceTextfieldText(mNativeClass, msg.arg1, - msg.arg2, rep.mReplace, rep.mNewStart, - rep.mNewEnd, rep.mTextGeneration); - break; - - case PASS_TO_JS: { - JSKeyData jsData = (JSKeyData) msg.obj; - KeyEvent evt = jsData.mEvent; - int keyCode = evt.getKeyCode(); - int keyValue = evt.getUnicodeChar(); - int generation = msg.arg1; - passToJs(mNativeClass, - generation, - jsData.mCurrentText, - keyCode, - keyValue, - evt.isDown(), evt.isShiftPressed(), - evt.isAltPressed(), evt.isSymPressed()); - break; - } - - case SAVE_DOCUMENT_STATE: { - nativeSaveDocumentState(mNativeClass); - break; - } - - case CLEAR_SSL_PREF_TABLE: - // FIXME: This will not work for connections currently in use, as - // they cache the certificate responses. See http://b/5324235. - SslCertLookupTable.getInstance().clear(); - nativeCloseIdleConnections(mNativeClass); - break; - - case SET_ACTIVE: - nativeSetFocusControllerActive(mNativeClass, msg.arg1 == 1); - break; - - case ADD_JS_INTERFACE: - JSInterfaceData jsData = (JSInterfaceData) msg.obj; - mBrowserFrame.addJavascriptInterface(jsData.mObject, - jsData.mInterfaceName, jsData.mRequireAnnotation); - break; - - case REMOVE_JS_INTERFACE: - jsData = (JSInterfaceData) msg.obj; - mBrowserFrame.removeJavascriptInterface( - jsData.mInterfaceName); - break; - - case REQUEST_EXT_REPRESENTATION: - mBrowserFrame.externalRepresentation( - (Message) msg.obj); - break; - - case REQUEST_DOC_AS_TEXT: - mBrowserFrame.documentAsText((Message) msg.obj); - break; - - case SET_MOVE_MOUSE: - nativeMoveMouse(mNativeClass, msg.arg1, msg.arg2); - break; - - case REQUEST_CURSOR_HREF: { - WebKitHitTest hit = performHitTest(msg.arg1, msg.arg2, 1, false); - Message hrefMsg = (Message) msg.obj; - Bundle data = hrefMsg.getData(); - data.putString(FocusNodeHref.URL,hit.mLinkUrl); - data.putString(FocusNodeHref.TITLE, hit.mAnchorText); - data.putString(FocusNodeHref.SRC, hit.mImageUrl); - hrefMsg.sendToTarget(); - break; - } - - case DOC_HAS_IMAGES: - Message imageResult = (Message) msg.obj; - imageResult.arg1 = - mBrowserFrame.documentHasImages() ? 1 : 0; - imageResult.sendToTarget(); - break; - - case DELETE_SELECTION: - TextSelectionData deleteSelectionData - = (TextSelectionData) msg.obj; - nativeDeleteSelection(mNativeClass, - deleteSelectionData.mStart, deleteSelectionData.mEnd, msg.arg1); - break; - - case SET_SELECTION: - nativeSetSelection(mNativeClass, msg.arg1, msg.arg2); - break; - - case MODIFY_SELECTION: - mTextSelectionChangeReason - = TextSelectionData.REASON_ACCESSIBILITY_INJECTOR; - final SomeArgs args = (SomeArgs) msg.obj; - final String modifiedSelectionString = nativeModifySelection( - mNativeClass, args.argi1, args.argi2); - // If accessibility is on, the main thread may be - // waiting for a response. Send on webcore thread. - mWebViewClassic.handleSelectionChangedWebCoreThread( - modifiedSelectionString, args.argi3); - args.recycle(); - mTextSelectionChangeReason - = TextSelectionData.REASON_UNKNOWN; - break; - - case LISTBOX_CHOICES: - SparseBooleanArray choices = (SparseBooleanArray) - msg.obj; - int choicesSize = msg.arg1; - boolean[] choicesArray = new boolean[choicesSize]; - for (int c = 0; c < choicesSize; c++) { - choicesArray[c] = choices.get(c); - } - nativeSendListBoxChoices(mNativeClass, - choicesArray, choicesSize); - break; - - case SINGLE_LISTBOX_CHOICE: - nativeSendListBoxChoice(mNativeClass, msg.arg1); - break; - - case SET_BACKGROUND_COLOR: - nativeSetBackgroundColor(mNativeClass, msg.arg1); - break; - - case DUMP_DOMTREE: - nativeDumpDomTree(mNativeClass, msg.arg1 == 1); - break; - - case DUMP_RENDERTREE: - nativeDumpRenderTree(mNativeClass, msg.arg1 == 1); - break; - - case SET_JS_FLAGS: - nativeSetJsFlags(mNativeClass, (String)msg.obj); - break; - - case CONTENT_INVALIDATE_ALL: - nativeContentInvalidateAll(mNativeClass); - break; - - case SAVE_WEBARCHIVE: - WebViewClassic.SaveWebArchiveMessage saveMessage = - (WebViewClassic.SaveWebArchiveMessage)msg.obj; - saveMessage.mResultFile = - saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname); - mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget(); - break; - - case GEOLOCATION_PERMISSIONS_PROVIDE: - GeolocationPermissionsData data = - (GeolocationPermissionsData) msg.obj; - nativeGeolocationPermissionsProvide(mNativeClass, - data.mOrigin, data.mAllow, data.mRemember); - break; - - case CLEAR_CONTENT: - // Clear the view so that onDraw() will draw nothing - // but white background - // (See public method WebView.clearView) - clearContent(); - break; - - case MESSAGE_RELAY: - ((Message) msg.obj).sendToTarget(); - break; - - case POPULATE_VISITED_LINKS: - nativeProvideVisitedHistory(mNativeClass, (String[])msg.obj); - break; - - case HIDE_FULLSCREEN: - nativeFullScreenPluginHidden(mNativeClass, msg.arg1); - break; - - case PLUGIN_SURFACE_READY: - nativePluginSurfaceReady(mNativeClass); - break; - - case NOTIFY_ANIMATION_STARTED: - nativeNotifyAnimationStarted(mNativeClass); - break; - - case ADD_PACKAGE_NAMES: - if (BrowserFrame.sJavaBridge == null) { - throw new IllegalStateException("No WebView " + - "has been created in this process!"); - } - BrowserFrame.sJavaBridge.addPackageNames( - (Set<String>) msg.obj); - break; - - case SET_USE_MOCK_GEOLOCATION: - setUseMockGeolocation(); - break; - - case SET_USE_MOCK_DEVICE_ORIENTATION: - setUseMockDeviceOrientation(); - break; - - case AUTOFILL_FORM: - nativeAutoFillForm(mNativeClass, msg.arg1); - mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.AUTOFILL_COMPLETE, null).sendToTarget(); - break; - - case EXECUTE_JS: - if (msg.obj instanceof String) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.d(LOGTAG, "Executing JS : " + msg.obj); - } - mBrowserFrame.stringByEvaluatingJavaScriptFromString( - (String) msg.obj); - } - break; - case SCROLL_LAYER: - int nativeLayer = msg.arg1; - Rect rect = (Rect) msg.obj; - nativeScrollLayer(mNativeClass, nativeLayer, - rect); - break; - - case DELETE_TEXT: { - int[] handles = (int[]) msg.obj; - nativeDeleteText(mNativeClass, handles[0], - handles[1], handles[2], handles[3]); - break; - } - case COPY_TEXT: { - int[] handles = (int[]) msg.obj; - String copiedText = nativeGetText(mNativeClass, - handles[0], handles[1], handles[2], - handles[3]); - if (copiedText != null) { - mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.COPY_TO_CLIPBOARD, copiedText) - .sendToTarget(); - } - break; - } - case INSERT_TEXT: - nativeInsertText(mNativeClass, (String) msg.obj); - break; - case SELECT_TEXT: { - int handleId = (Integer) msg.obj; - nativeSelectText(mNativeClass, handleId, - msg.arg1, msg.arg2); - break; - } - case SELECT_WORD_AT: { - mTextSelectionChangeReason - = TextSelectionData.REASON_SELECT_WORD; - int x = msg.arg1; - int y = msg.arg2; - if (!nativeSelectWordAt(mNativeClass, x, y)) { - mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE) - .sendToTarget(); - } - mTextSelectionChangeReason - = TextSelectionData.REASON_UNKNOWN; - break; - } - case SELECT_ALL: - nativeSelectAll(mNativeClass); - break; - case FIND_ALL: { - FindAllRequest request = (FindAllRequest)msg.obj; - if (request != null) { - int matchCount = nativeFindAll(mNativeClass, request.mSearchText); - int matchIndex = nativeFindNext(mNativeClass, true); - synchronized (request) { - request.mMatchCount = matchCount; - request.mMatchIndex = matchIndex; - request.notify(); - } - } else { - nativeFindAll(mNativeClass, null); - } - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget(); - break; - } - case FIND_NEXT: { - FindAllRequest request = (FindAllRequest)msg.obj; - int matchIndex = nativeFindNext(mNativeClass, msg.arg1 != 0); - synchronized (request) { - request.mMatchIndex = matchIndex; - } - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget(); - break; - } - case SET_INITIAL_FOCUS: - nativeSetInitialFocus(mNativeClass, msg.arg1); - break; - case SAVE_VIEW_STATE: - SaveViewStateRequest request = (SaveViewStateRequest) msg.obj; - saveViewState(request.mStream, request.mCallback); - break; - } - } - - }; - // Take all queued messages and resend them to the new handler. - synchronized (this) { - int size = mMessages.size(); - for (int i = 0; i < size; i++) { - mHandler.sendMessage(mMessages.get(i)); - } - mMessages = null; - } - } - - @Override - public Looper getWebKitLooper() { - return mHandler.getLooper(); - } - - @Override - public boolean dispatchWebKitEvent(WebViewInputDispatcher dispatcher, - MotionEvent event, int eventType, int flags) { - if (mNativeClass == 0) { - return false; - } - switch (eventType) { - case WebViewInputDispatcher.EVENT_TYPE_HIT_TEST: - int x = Math.round(event.getX()); - int y = Math.round(event.getY()); - WebKitHitTest hit = performHitTest(x, y, - mWebViewClassic.getScaledNavSlop(), true); - mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.HIT_TEST_RESULT, hit).sendToTarget(); - return false; - - case WebViewInputDispatcher.EVENT_TYPE_CLICK: - return nativeMouseClick(mNativeClass); - - case WebViewInputDispatcher.EVENT_TYPE_TOUCH: { - int count = event.getPointerCount(); - int[] idArray = new int[count]; - int[] xArray = new int[count]; - int[] yArray = new int[count]; - for (int i = 0; i < count; i++) { - idArray[i] = event.getPointerId(i); - xArray[i] = (int) event.getX(i); - yArray[i] = (int) event.getY(i); - } - int touchFlags = nativeHandleTouchEvent(mNativeClass, - event.getActionMasked(), - idArray, xArray, yArray, count, - event.getActionIndex(), event.getMetaState()); - if (touchFlags == 0 - && event.getActionMasked() != MotionEvent.ACTION_CANCEL - && (flags & WebViewInputDispatcher.FLAG_PRIVATE) == 0) { - dispatcher.skipWebkitForRemainingTouchStream(); - } - return (touchFlags & TOUCH_FLAG_PREVENT_DEFAULT) > 0; - } - - default: - return false; - } - } - - /** - * Send a message internally to the queue or to the handler - */ - private synchronized void sendMessage(Message msg) { - if (mBlockMessages) { - return; - } - if (mMessages != null) { - mMessages.add(msg); - } else { - mHandler.sendMessage(msg); - } - } - - private synchronized void removeMessages(int what) { - if (mBlockMessages) { - return; - } - if (what == EventHub.WEBKIT_DRAW) { - mDrawIsScheduled = false; - } - if (mMessages != null) { - Iterator<Message> iter = mMessages.iterator(); - while (iter.hasNext()) { - Message m = iter.next(); - if (m.what == what) { - iter.remove(); - } - } - } else { - mHandler.removeMessages(what); - } - } - - private synchronized void sendMessageDelayed(Message msg, long delay) { - if (mBlockMessages) { - return; - } - mHandler.sendMessageDelayed(msg, delay); - } - - /** - * Send a message internally to the front of the queue. - */ - private synchronized void sendMessageAtFrontOfQueue(Message msg) { - if (mBlockMessages) { - return; - } - if (mMessages != null) { - mMessages.add(0, msg); - } else { - mHandler.sendMessageAtFrontOfQueue(msg); - } - } - - /** - * Remove all the messages. - */ - private synchronized void removeMessages() { - // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed - mDrawIsScheduled = false; - if (mMessages != null) { - mMessages.clear(); - } else { - mHandler.removeCallbacksAndMessages(null); - } - } - - /** - * Block sending messages to the EventHub. - */ - private synchronized void blockMessages() { - mBlockMessages = true; - } - } - - //------------------------------------------------------------------------- - // Methods called by host activity (in the same thread) - //------------------------------------------------------------------------- - - void stopLoading() { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); - if (mBrowserFrame != null) { - mBrowserFrame.stopLoading(); - } - } - - //------------------------------------------------------------------------- - // Methods called by WebView - // If it refers to local variable, it needs synchronized(). - // If it needs WebCore, it has to send message. - //------------------------------------------------------------------------- - - /** - * @hide - */ - public void sendMessage(Message msg) { - mEventHub.sendMessage(msg); - } - - void sendMessages(ArrayList<Message> messages) { - synchronized (mEventHub) { - for (int i = 0; i < messages.size(); i++) { - mEventHub.sendMessage(messages.get(i)); - } - } - } - - void sendMessage(int what) { - mEventHub.sendMessage(Message.obtain(null, what)); - } - - void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) { - mEventHub.sendMessageAtFrontOfQueue(Message.obtain( - null, what, arg1, arg2, obj)); - } - - void sendMessage(int what, Object obj) { - mEventHub.sendMessage(Message.obtain(null, what, obj)); - } - - void sendMessage(int what, int arg1) { - // just ignore the second argument (make it 0) - mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); - } - - void sendMessage(int what, int arg1, int arg2) { - mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); - } - - void sendMessage(int what, int arg1, Object obj) { - // just ignore the second argument (make it 0) - mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); - } - - void sendMessage(int what, int arg1, int arg2, Object obj) { - mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); - } - - void sendMessageAtFrontOfQueue(int what, Object obj) { - mEventHub.sendMessageAtFrontOfQueue(Message.obtain( - null, what, obj)); - } - - void sendMessageDelayed(int what, Object obj, long delay) { - mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); - } - - void removeMessages(int what) { - mEventHub.removeMessages(what); - } - - void removeMessages() { - mEventHub.removeMessages(); - } - - /** - * Sends a DESTROY message to WebCore. - * Called from UI thread. - */ - void destroy() { - synchronized (mEventHub) { - // send DESTROY to front of queue - // PAUSE/RESUME timers will still be processed even if they get handled later - mEventHub.mDestroying = true; - mEventHub.sendMessageAtFrontOfQueue( - Message.obtain(null, EventHub.DESTROY)); - mEventHub.blockMessages(); - } - } - - //------------------------------------------------------------------------- - // WebViewCore private methods - //------------------------------------------------------------------------- - - private WebKitHitTest performHitTest(int x, int y, int slop, boolean moveMouse) { - WebKitHitTest hit = nativeHitTest(mNativeClass, x, y, slop, moveMouse); - hit.mHitTestX = x; - hit.mHitTestY = y; - hit.mHitTestSlop = slop; - hit.mHitTestMovedMouse = moveMouse; - return hit; - } - - private void clearCache(boolean includeDiskFiles) { - mBrowserFrame.clearCache(); - } - - private void loadUrl(String url, Map<String, String> extraHeaders) { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); - mBrowserFrame.loadUrl(url, extraHeaders); - } - - private String saveWebArchive(String filename, boolean autoname) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname); - } - return mBrowserFrame.saveWebArchive(filename, autoname); - } - - private void key(KeyEvent evt, int canTakeFocusDirection, boolean isDown) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " - + evt); - } - mChromeCanFocusDirection = canTakeFocusDirection; - int keyCode = evt.getKeyCode(); - int unicodeChar = evt.getUnicodeChar(); - - if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null - && evt.getCharacters().length() > 0) { - // we should only receive individual complex characters - unicodeChar = evt.getCharacters().codePointAt(0); - } - - boolean handled = nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(), - evt.isShiftPressed(), evt.isAltPressed(), - evt.isSymPressed(), isDown); - mChromeCanFocusDirection = 0; - if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) { - if (keyCode >= KeyEvent.KEYCODE_DPAD_UP - && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { - if (canTakeFocusDirection != 0 && isDown) { - Message m = mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.TAKE_FOCUS); - m.arg1 = canTakeFocusDirection; - m.sendToTarget(); - } - return; - } - // bubble up the event handling - // but do not bubble up the ENTER key, which would open the search - // bar without any text. - mCallbackProxy.onUnhandledKeyEvent(evt); - } - } - - private void keyPress(int unicodeChar) { - nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, true); - nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, false); - } - - // These values are used to avoid requesting a layout based on old values - private int mCurrentViewWidth = 0; - private int mCurrentViewHeight = 0; - private float mCurrentViewScale = 1.0f; - - // notify webkit that our virtual view size changed size (after inv-zoom) - private void viewSizeChanged(WebViewClassic.ViewSizeData data) { - int w = data.mWidth; - int h = data.mHeight; - int textwrapWidth = data.mTextWrapWidth; - float scale = data.mScale; - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h - + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); - } - if (w == 0) { - Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); - return; - } - int width = calculateWindowWidth(w); - int height = h; - if (width != w) { - float heightWidthRatio = data.mHeightWidthRatio; - float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w; - height = Math.round(ratio * width); - } - int screenHeight = data.mActualViewHeight > 0 ? data.mActualViewHeight : h; - nativeSetSize(mNativeClass, width, height, textwrapWidth, scale, - w, screenHeight, data.mAnchorX, data.mAnchorY, data.mIgnoreHeight); - // Remember the current width and height - boolean needInvalidate = (mCurrentViewWidth == 0); - mCurrentViewWidth = w; - mCurrentViewHeight = h; - mCurrentViewScale = scale; - if (needInvalidate) { - // ensure {@link #webkitDraw} is called as we were blocking in - // {@link #contentDraw} when mCurrentViewWidth is 0 - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); - contentDraw(); - } - } - - // Calculate width to be used in webkit window. - private int calculateWindowWidth(int viewWidth) { - int width = viewWidth; - if (mSettings.getUseWideViewPort()) { - if (mViewportWidth == -1) { - // Fixed viewport width. - width = WebViewClassic.DEFAULT_VIEWPORT_WIDTH; - } else if (mViewportWidth > 0) { - // Use website specified or desired fixed viewport width. - width = mViewportWidth; - } else { - // For mobile web site. - width = Math.round(mWebViewClassic.getViewWidth() / - mWebViewClassic.getDefaultZoomScale()); - } - } - return width; - } - - // Utility method for exceededDatabaseQuota callback. Computes the sum - // of WebSQL database quota for all origins. - private long getUsedQuota() { - WebStorageClassic webStorage = WebStorageClassic.getInstance(); - Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); - - if (origins == null) { - return 0; - } - long usedQuota = 0; - for (WebStorage.Origin website : origins) { - usedQuota += website.getQuota(); - } - return usedQuota; - } - - // Used to avoid posting more than one draw message. - private boolean mDrawIsScheduled; - - // Used to suspend drawing. - private boolean mDrawIsPaused; - - // mInitialViewState is set by didFirstLayout() and then reset in the - // next webkitDraw after passing the state to the UI thread. - private ViewState mInitialViewState = null; - private boolean mFirstLayoutForNonStandardLoad; - - static class ViewState { - float mMinScale; - float mMaxScale; - float mViewScale; - float mTextWrapScale; - float mDefaultScale; - int mScrollX; - int mScrollY; - boolean mMobileSite; - boolean mIsRestored; - boolean mShouldStartScrolledRight; - } - - static class DrawData { - DrawData() { - mBaseLayer = 0; - mContentSize = new Point(); - } - int mBaseLayer; - // view size that was used by webkit during the most recent layout - Point mViewSize; - Point mContentSize; - int mMinPrefWidth; - // only non-null if it is for the first picture set after the first layout - ViewState mViewState; - boolean mFirstLayoutForNonStandardLoad; - } - - DrawData mLastDrawData = null; - - private Object m_skipDrawFlagLock = new Object(); - private boolean m_skipDrawFlag = false; - private boolean m_drawWasSkipped = false; - - void pauseWebKitDraw() { - synchronized (m_skipDrawFlagLock) { - if (!m_skipDrawFlag) { - m_skipDrawFlag = true; - } - } - } - - void resumeWebKitDraw() { - synchronized (m_skipDrawFlagLock) { - if (m_skipDrawFlag && m_drawWasSkipped) { - // a draw was dropped, send a retry - m_drawWasSkipped = false; - mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); - } - m_skipDrawFlag = false; - } - } - - private void webkitDraw() { - synchronized (m_skipDrawFlagLock) { - if (m_skipDrawFlag) { - m_drawWasSkipped = true; - return; - } - } - - mDrawIsScheduled = false; - DrawData draw = new DrawData(); - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); - draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mContentSize); - if (draw.mBaseLayer == 0) { - if (mWebViewClassic != null && !mWebViewClassic.isPaused()) { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message"); - mEventHub.sendMessageDelayed(Message.obtain(null, EventHub.WEBKIT_DRAW), 10); - } else { - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused"); - } - return; - } - mLastDrawData = draw; - webkitDraw(draw); - } - - private void webkitDraw(DrawData draw) { - if (mWebViewClassic != null) { - draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight); - if (mSettings.getUseWideViewPort()) { - draw.mMinPrefWidth = Math.max( - mViewportWidth == -1 ? WebViewClassic.DEFAULT_VIEWPORT_WIDTH - : (mViewportWidth == 0 ? mCurrentViewWidth - : mViewportWidth), - nativeGetContentMinPrefWidth(mNativeClass)); - } - if (mInitialViewState != null) { - draw.mViewState = mInitialViewState; - mInitialViewState = null; - } - if (mFirstLayoutForNonStandardLoad) { - draw.mFirstLayoutForNonStandardLoad = true; - mFirstLayoutForNonStandardLoad = false; - } - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); - pauseWebKitDraw(); - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget(); - } - } - - private void saveViewState(OutputStream stream, - ValueCallback<Boolean> callback) { - // TODO: Create a native method to do this better without overloading - // the draw path (and fix saving <canvas>) - DrawData draw = new DrawData(); - if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "saveViewState start"); - draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mContentSize); - boolean result = false; - try { - result = ViewStateSerializer.serializeViewState(stream, draw); - } catch (Throwable t) { - Log.w(LOGTAG, "Failed to save view state", t); - } - callback.onReceiveValue(result); - if (draw.mBaseLayer != 0) { - if (mDrawIsScheduled) { - mDrawIsScheduled = false; - mEventHub.removeMessages(EventHub.WEBKIT_DRAW); - } - mLastDrawData = draw; - webkitDraw(draw); - } - } - - static void reducePriority() { - // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages - sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); - sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); - sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler - .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); - } - - static void resumePriority() { - // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages - sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); - sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); - sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler - .obtainMessage(WebCoreThread.RESUME_PRIORITY)); - } - - static void sendStaticMessage(int messageType, Object argument) { - if (sWebCoreHandler == null) - return; - - sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument)); - } - - static void pauseUpdatePicture(WebViewCore core) { - // Note: there is one possible failure mode. If pauseUpdatePicture() is - // called from UI thread while WEBKIT_DRAW is just pulled out of the - // queue in WebCore thread to be executed. Then update won't be blocked. - if (core != null) { - if (!core.getSettings().enableSmoothTransition()) return; - - synchronized (core) { - if (core.mNativeClass == 0) { - Log.w(LOGTAG, "Cannot pauseUpdatePicture, core destroyed or not initialized!"); - return; - } - core.mDrawIsPaused = true; - } - } - - } - - static void resumeUpdatePicture(WebViewCore core) { - if (core != null) { - // if mDrawIsPaused is true, ignore the setting, continue to resume - if (!core.mDrawIsPaused) - return; - - synchronized (core) { - if (core.mNativeClass == 0) { - Log.w(LOGTAG, "Cannot resumeUpdatePicture, core destroyed!"); - return; - } - core.mDrawIsPaused = false; - // always redraw on resume to reenable gif animations - core.mDrawIsScheduled = false; - } - } - } - - static boolean isUpdatePicturePaused(WebViewCore core) { - return core != null ? core.mDrawIsPaused : false; - } - - ////////////////////////////////////////////////////////////////////////// - - private void restoreState(int index) { - WebBackForwardListClassic list = mCallbackProxy.getBackForwardList(); - int size = list.getSize(); - for (int i = 0; i < size; i++) { - list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); - } - mBrowserFrame.mLoadInitFromJava = true; - WebBackForwardListClassic.restoreIndex(mBrowserFrame.mNativeFrame, index); - mBrowserFrame.mLoadInitFromJava = false; - } - - //------------------------------------------------------------------------- - // Implement abstract methods in WebViewCore, native WebKit callback part - //------------------------------------------------------------------------- - - // called from JNI or WebView thread - /* package */ void contentDraw() { - synchronized (this) { - if (mWebViewClassic == null || mBrowserFrame == null) { - // We were destroyed - return; - } - // don't update the Picture until we have an initial width and finish - // the first layout - if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { - return; - } - // only fire an event if this is our first request - if (mDrawIsScheduled) return; - mDrawIsScheduled = true; - mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); - } - } - - // called by JNI - private void contentScrollTo(int x, int y, boolean animate, - boolean onlyIfImeIsShowing) { - if (!mBrowserFrame.firstLayoutDone()) { - /* - * WebKit restore state will be called before didFirstLayout(), - * remember the position as it has to be applied after restoring - * zoom factor which is controlled by screenWidth. - */ - mRestoredX = x; - mRestoredY = y; - return; - } - if (mWebViewClassic != null) { - Message msg = Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0, - onlyIfImeIsShowing ? 1 : 0, new Point(x, y)); - if (mDrawIsScheduled) { - mEventHub.sendMessage(Message.obtain(null, - EventHub.MESSAGE_RELAY, msg)); - } else { - msg.sendToTarget(); - } - } - } - - // called by JNI - private void sendNotifyProgressFinished() { - contentDraw(); - } - - /* Called by JNI. The coordinates are in doc coordinates, so they need to - be scaled before they can be used by the view system, which happens - in WebView since it (and its thread) know the current scale factor. - */ - private void sendViewInvalidate(int left, int top, int right, int bottom) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.INVAL_RECT_MSG_ID, - new Rect(left, top, right, bottom)).sendToTarget(); - } - } - - private static boolean mRepaintScheduled = false; - - /* - * Called by the WebView thread - */ - /* package */ void signalRepaintDone() { - mRepaintScheduled = false; - } - - // Gets the WebViewClassic corresponding to this WebViewCore. Note that the - // WebViewClassic object must only be used on the UI thread. - /* package */ WebViewClassic getWebViewClassic() { - return mWebViewClassic; - } - - // Called by JNI - private WebView getWebView() { - return mWebViewClassic.getWebView(); - } - - // Called by JNI - private void sendPluginDrawMsg() { - sendMessage(EventHub.PLUGIN_SURFACE_READY); - } - - private native void setViewportSettingsFromNative(int nativeClass); - - // called by JNI - private void didFirstLayout(boolean standardLoad) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); - } - - mBrowserFrame.didFirstLayout(); - - if (mWebViewClassic == null) return; - - boolean updateViewState = standardLoad || mIsRestored; - setupViewport(updateViewState); - // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will - // be called after the WebView updates its state. If updateRestoreState - // is false, start to draw now as it is ready. - if (!updateViewState) { - mWebViewClassic.mViewManager.postReadyToDrawAll(); - } - - // remove the touch highlight when moving to a new page - mWebViewClassic.mPrivateHandler.sendEmptyMessage( - WebViewClassic.HIT_TEST_RESULT); - - // reset the scroll position, the restored offset and scales - mRestoredX = mRestoredY = 0; - mIsRestored = false; - mRestoredScale = mRestoredTextWrapScale = 0; - } - - // called by JNI - private void updateViewport() { - // Update viewport asap to make sure we get correct one. - setupViewport(true); - } - - static float getFixedDisplayDensity(Context context) { - // We make bad assumptions about multiplying and dividing density by 100, - // force them to be true with this hack - float density = context.getResources().getDisplayMetrics().density; - return ((int) (density * 100)) / 100.0f; - } - - private void setupViewport(boolean updateViewState) { - if (mWebViewClassic == null || mSettings == null) { - // We've been destroyed or are being destroyed, return early - return; - } - // set the viewport settings from WebKit - setViewportSettingsFromNative(mNativeClass); - - // clamp initial scale - if (mViewportInitialScale > 0) { - if (mViewportMinimumScale > 0) { - mViewportInitialScale = Math.max(mViewportInitialScale, - mViewportMinimumScale); - } - if (mViewportMaximumScale > 0) { - mViewportInitialScale = Math.min(mViewportInitialScale, - mViewportMaximumScale); - } - } - - if (mSettings.forceUserScalable()) { - mViewportUserScalable = true; - if (mViewportInitialScale > 0) { - if (mViewportMinimumScale > 0) { - mViewportMinimumScale = Math.min(mViewportMinimumScale, - mViewportInitialScale / 2); - } - if (mViewportMaximumScale > 0) { - mViewportMaximumScale = Math.max(mViewportMaximumScale, - mViewportInitialScale * 2); - } - } else { - if (mViewportMinimumScale > 0) { - mViewportMinimumScale = Math.min(mViewportMinimumScale, 50); - } - if (mViewportMaximumScale > 0) { - mViewportMaximumScale = Math.max(mViewportMaximumScale, 200); - } - } - } - - // adjust the default scale to match the densityDpi - float adjust = 1.0f; - if (mViewportDensityDpi == -1) { - adjust = getFixedDisplayDensity(mContext); - } else if (mViewportDensityDpi > 0) { - adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi - / mViewportDensityDpi; - adjust = ((int) (adjust * 100)) / 100.0f; - } - - // Remove any update density messages in flight. - // If the density is indeed different from WebView's default scale, - // a new message will be queued. - mWebViewClassic.mPrivateHandler.removeMessages( - WebViewClassic.UPDATE_ZOOM_DENSITY); - if (adjust != mWebViewClassic.getDefaultZoomScale()) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget(); - } - int defaultScale = (int) (adjust * 100); - - if (mViewportInitialScale > 0) { - mViewportInitialScale *= adjust; - } - if (mViewportMinimumScale > 0) { - mViewportMinimumScale *= adjust; - } - if (mViewportMaximumScale > 0) { - mViewportMaximumScale *= adjust; - } - - // infer the values if they are not defined. - if (mViewportWidth == 0) { - if (mViewportInitialScale == 0) { - mViewportInitialScale = defaultScale; - } - } - if (mViewportUserScalable == false) { - mViewportInitialScale = defaultScale; - mViewportMinimumScale = defaultScale; - mViewportMaximumScale = defaultScale; - } - if (mViewportMinimumScale > mViewportInitialScale - && mViewportInitialScale != 0) { - mViewportMinimumScale = mViewportInitialScale; - } - if (mViewportMaximumScale > 0 - && mViewportMaximumScale < mViewportInitialScale) { - mViewportMaximumScale = mViewportInitialScale; - } - if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { - mViewportWidth = 0; - } - - // if mViewportWidth is 0, it means device-width, always update. - if (mViewportWidth != 0 && !updateViewState) { - // For non standard load, since updateViewState will be false. - mFirstLayoutForNonStandardLoad = true; - ViewState viewState = new ViewState(); - viewState.mMinScale = mViewportMinimumScale / 100.0f; - viewState.mMaxScale = mViewportMaximumScale / 100.0f; - viewState.mDefaultScale = adjust; - // as mViewportWidth is not 0, it is not mobile site. - viewState.mMobileSite = false; - // for non-mobile site, we don't need minPrefWidth, set it as 0 - viewState.mScrollX = 0; - viewState.mShouldStartScrolledRight = false; - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget(); - return; - } - - // now notify webview - // webViewWidth refers to the width in the view system - int webViewWidth; - // viewportWidth refers to the width in the document system - int viewportWidth = mCurrentViewWidth; - if (viewportWidth == 0) { - // this may happen when WebView just starts. This is not perfect as - // we call WebView method from WebCore thread. But not perfect - // reference is better than no reference. - webViewWidth = mWebViewClassic.getViewWidth(); - viewportWidth = (int) (webViewWidth / adjust); - if (viewportWidth == 0) { - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "Can't get the viewWidth yet"); - } - } - } else { - webViewWidth = Math.round(viewportWidth * mCurrentViewScale); - } - mInitialViewState = new ViewState(); - mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f; - mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f; - mInitialViewState.mDefaultScale = adjust; - mInitialViewState.mScrollX = mRestoredX; - mInitialViewState.mScrollY = mRestoredY; - mInitialViewState.mShouldStartScrolledRight = (mRestoredX == 0) - && (mRestoredY == 0) - && (mBrowserFrame != null) - && mBrowserFrame.getShouldStartScrolledRight(); - - mInitialViewState.mMobileSite = (0 == mViewportWidth); - if (mIsRestored) { - mInitialViewState.mIsRestored = true; - mInitialViewState.mViewScale = mRestoredScale; - if (mRestoredTextWrapScale > 0) { - mInitialViewState.mTextWrapScale = mRestoredTextWrapScale; - } else { - mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale; - } - } else { - if (mViewportInitialScale > 0) { - mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = - mViewportInitialScale / 100.0f; - } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth && - !getSettings().getUseFixedViewport()) { - mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = - (float) webViewWidth / mViewportWidth; - } else { - mInitialViewState.mTextWrapScale = adjust; - if (mSettings.getUseWideViewPort()) { - // 0 will trigger WebView to turn on zoom overview mode - mInitialViewState.mViewScale = 0; - } else { - mInitialViewState.mViewScale = adjust; - } - } - } - - if (mWebViewClassic.mHeightCanMeasure) { - // Trick to ensure that the Picture has the exact height for the - // content by forcing to layout with 0 height after the page is - // ready, which is indicated by didFirstLayout. This is essential to - // get rid of the white space in the GMail which uses WebView for - // message view. - mWebViewClassic.mLastHeightSent = 0; - // Send a negative scale to indicate that WebCore should reuse - // the current scale - WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData(); - data.mWidth = mWebViewClassic.mLastWidthSent; - data.mHeight = 0; - // if mHeightCanMeasure is true, getUseWideViewPort() can't be - // true. It is safe to use mWidth for mTextWrapWidth. - data.mTextWrapWidth = data.mWidth; - data.mScale = -1.0f; - data.mIgnoreHeight = false; - data.mAnchorX = data.mAnchorY = 0; - // send VIEW_SIZE_CHANGED to the front of the queue so that we can - // avoid pushing the wrong picture to the WebView side. If there is - // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, - // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED - // in the queue, as mLastHeightSent has been updated here, we may - // miss the requestLayout in WebView side after the new picture. - mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); - mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, - EventHub.VIEW_SIZE_CHANGED, data)); - } else { - if (viewportWidth == 0) { - // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView - // to WebViewCore - mWebViewClassic.mLastWidthSent = 0; - } else { - WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData(); - // mViewScale as 0 means it is in zoom overview mode. So we don't - // know the exact scale. If mRestoredScale is non-zero, use it; - // otherwise just use mTextWrapScale as the initial scale. - float tentativeScale = mInitialViewState.mViewScale; - if (tentativeScale == 0) { - // The following tries to figure out more correct view scale - // and text wrap scale to be sent to webkit, by using some - // knowledge from web settings and zoom manager. - - // Calculated window width will be used to guess the scale - // in zoom overview mode. - tentativeScale = mInitialViewState.mTextWrapScale; - int tentativeViewWidth = Math.round(webViewWidth / tentativeScale); - int windowWidth = calculateWindowWidth(tentativeViewWidth); - // In viewport setup time, since no content width is known, we assume - // the windowWidth will be the content width, to get a more likely - // zoom overview scale. - data.mScale = (float) webViewWidth / windowWidth; - if (!mSettings.getLoadWithOverviewMode()) { - // If user choose non-overview mode. - data.mScale = Math.max(data.mScale, tentativeScale); - } - if (mSettings.isNarrowColumnLayout()) { - // In case of automatic text reflow in fixed view port mode. - mInitialViewState.mTextWrapScale = - mWebViewClassic.computeReadingLevelScale(data.mScale); - } - } else { - // Scale is given such as when page is restored, use it. - data.mScale = tentativeScale; - } - if (DebugFlags.WEB_VIEW_CORE) { - Log.v(LOGTAG, "setupViewport" - + " mRestoredScale=" + mRestoredScale - + " mViewScale=" + mInitialViewState.mViewScale - + " mTextWrapScale=" + mInitialViewState.mTextWrapScale - + " data.mScale= " + data.mScale - ); - } - data.mWidth = Math.round(webViewWidth / data.mScale); - // We may get a call here when mCurrentViewHeight == 0 if webcore completes the - // first layout before we sync our webview dimensions to it. In that case, we - // request the real height of the webview. This is not a perfect solution as we - // are calling a WebView method from the WebCore thread. But this is preferable - // to syncing an incorrect height. - data.mHeight = mCurrentViewHeight == 0 ? - Math.round(mWebViewClassic.getViewHeight() / data.mScale) - : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth); - data.mTextWrapWidth = Math.round(webViewWidth - / mInitialViewState.mTextWrapScale); - data.mIgnoreHeight = false; - data.mAnchorX = data.mAnchorY = 0; - // send VIEW_SIZE_CHANGED to the front of the queue so that we - // can avoid pushing the wrong picture to the WebView side. - mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); - // Let webkit know the scale and inner width/height immediately - // in viewport setup time to avoid wrong information. - viewSizeChanged(data); - } - } - } - - // called by JNI - private void restoreScale(float scale, float textWrapScale) { - if (mBrowserFrame.firstLayoutDone() == false) { - mIsRestored = true; - mRestoredScale = scale; - if (mSettings.getUseWideViewPort()) { - mRestoredTextWrapScale = textWrapScale; - } - } - } - - // called by JNI - private void needTouchEvents(boolean need) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) - .sendToTarget(); - } - } - - // called by JNI - private void updateTextfield(int ptr, String text, int textGeneration) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, - textGeneration, text).sendToTarget(); - } - } - - private TextSelectionData createTextSelection(int start, int end, int selPtr) { - TextSelectionData data = new TextSelectionData(start, end, selPtr); - data.mSelectionReason = mTextSelectionChangeReason; - return data; - } - - // called by JNI - private void updateTextSelection(int pointer, int start, int end, - int textGeneration, int selectionPtr) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, - createTextSelection(start, end, selectionPtr)).sendToTarget(); - } - } - - // called by JNI - private void updateTextSizeAndScroll(int pointer, int width, int height, - int scrollX, int scrollY) { - if (mWebViewClassic != null) { - Rect rect = new Rect(-scrollX, -scrollY, width - scrollX, - height - scrollY); - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.EDIT_TEXT_SIZE_CHANGED, pointer, 0, rect) - .sendToTarget(); - } - } - - // called by JNI - private void clearTextEntry() { - if (mWebViewClassic == null) return; - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget(); - } - - // called by JNI - private void initEditField(int start, int end, int selectionPtr, - TextFieldInitData initData) { - if (mWebViewClassic == null) { - return; - } - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget(); - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, - initData.mFieldPointer, 0, - createTextSelection(start, end, selectionPtr)) - .sendToTarget(); - } - - private native void nativeRevealSelection(int nativeClass); - private native String nativeRequestLabel(int nativeClass, int framePtr, - int nodePtr); - /** - * Scroll the focused textfield to (xPercent, y) in document space - */ - private native void nativeScrollFocusedTextInput(int nativeClass, - float xPercent, int y); - - // these must be in document space (i.e. not scaled/zoomed). - private native void nativeSetScrollOffset(int nativeClass, - boolean sendScrollEvent, int dx, int dy); - - private native void nativeSetGlobalBounds(int nativeClass, int x, int y, - int w, int h); - - // called by JNI - private void requestListBox(String[] array, int[] enabledArray, - int[] selectedArray) { - if (mWebViewClassic != null) { - mWebViewClassic.requestListBox(array, enabledArray, selectedArray); - } - } - - // called by JNI - private void requestListBox(String[] array, int[] enabledArray, - int selection) { - if (mWebViewClassic != null) { - mWebViewClassic.requestListBox(array, enabledArray, selection); - } - - } - - // called by JNI - private void requestKeyboard(boolean showKeyboard) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, - WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) - .sendToTarget(); - } - } - - private void setWebTextViewAutoFillable(int queryId, String preview) { - if (mWebViewClassic != null) { - Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE, - new AutoFillData(queryId, preview)) - .sendToTarget(); - } - } - - Context getContext() { - return mContext; - } - - // called by JNI - private void keepScreenOn(boolean screenOn) { - if (mWebViewClassic != null) { - Message message = mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.SCREEN_ON); - message.arg1 = screenOn ? 1 : 0; - message.sendToTarget(); - } - } - - // called by JNI - private Class<?> getPluginClass(String libName, String clsName) { - - if (mWebViewClassic == null) { - return null; - } - - PluginManager pluginManager = PluginManager.getInstance(null); - - String pkgName = pluginManager.getPluginsAPKName(libName); - if (pkgName == null) { - Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); - return null; - } - - try { - return pluginManager.getPluginClass(pkgName, clsName); - } catch (NameNotFoundException e) { - Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); - } catch (ClassNotFoundException e) { - Log.e(LOGTAG, "Unable to find plugin class (" + clsName + - ") in the apk (" + pkgName + ")"); - } - - return null; - } - - // called by JNI. PluginWidget function to launch a full-screen view using a - // View object provided by the plugin class. - private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) { - if (mWebViewClassic == null) { - return; - } - - Message message = mWebViewClassic.mPrivateHandler.obtainMessage( - WebViewClassic.SHOW_FULLSCREEN); - message.obj = childView.mView; - message.arg1 = orientation; - message.arg2 = npp; - message.sendToTarget(); - } - - // called by JNI - private void hideFullScreenPlugin() { - if (mWebViewClassic == null) { - return; - } - mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN) - .sendToTarget(); - } - - private ViewManager.ChildView createSurface(View pluginView) { - if (mWebViewClassic == null) { - return null; - } - - if (pluginView == null) { - Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy"); - return null; - } - - // ensures the view system knows the view can redraw itself - pluginView.setWillNotDraw(false); - - if(pluginView instanceof SurfaceView) - ((SurfaceView)pluginView).setZOrderOnTop(true); - - ViewManager.ChildView view = mWebViewClassic.mViewManager.createView(); - view.mView = pluginView; - return view; - } - - // called by JNI. PluginWidget functions for creating an embedded View for - // the surface drawing model. - private ViewManager.ChildView addSurface(View pluginView, int x, int y, - int width, int height) { - ViewManager.ChildView view = createSurface(pluginView); - view.attachView(x, y, width, height); - return view; - } - - private void updateSurface(ViewManager.ChildView childView, int x, int y, - int width, int height) { - childView.attachView(x, y, width, height); - } - - private void destroySurface(ViewManager.ChildView childView) { - childView.removeView(); - } - - // called by JNI - static class ShowRectData { - int mLeft; - int mTop; - int mWidth; - int mHeight; - int mContentWidth; - int mContentHeight; - float mXPercentInDoc; - float mXPercentInView; - float mYPercentInDoc; - float mYPercentInView; - } - - private void showRect(int left, int top, int width, int height, - int contentWidth, int contentHeight, float xPercentInDoc, - float xPercentInView, float yPercentInDoc, float yPercentInView) { - if (mWebViewClassic != null) { - ShowRectData data = new ShowRectData(); - data.mLeft = left; - data.mTop = top; - data.mWidth = width; - data.mHeight = height; - data.mContentWidth = contentWidth; - data.mContentHeight = contentHeight; - data.mXPercentInDoc = xPercentInDoc; - data.mXPercentInView = xPercentInView; - data.mYPercentInDoc = yPercentInDoc; - data.mYPercentInView = yPercentInView; - Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID, - data).sendToTarget(); - } - } - - // called by JNI - private void centerFitRect(int x, int y, int width, int height) { - if (mWebViewClassic == null) { - return; - } - mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT, - new Rect(x, y, x + width, y + height)).sendToTarget(); - } - - // called by JNI - private void setScrollbarModes(int hMode, int vMode) { - if (mWebViewClassic == null) { - return; - } - mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES, - hMode, vMode).sendToTarget(); - } - - // called by JNI - private void selectAt(int x, int y) { - // TODO: Figure out what to do with this (b/6111818) - } - - private void setUseMockDeviceOrientation() { - mDeviceMotionAndOrientationManager.setUseMock(); - } - - private void setUseMockGeolocation() { - mMockGeolocation.setUseMock(); - } - - public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) { - mMockGeolocation.setPosition(latitude, longitude, accuracy); - } - - public void setMockGeolocationError(int code, String message) { - mMockGeolocation.setError(code, message); - } - - public void setMockGeolocationPermission(boolean allow) { - mMockGeolocation.setPermission(allow); - } - - public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, - boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { - mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha, - canProvideBeta, beta, canProvideGamma, gamma); - } - - protected DeviceMotionService getDeviceMotionService() { - if (mDeviceMotionService == null) { - mDeviceMotionService = - new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext); - } - return mDeviceMotionService; - } - - protected DeviceOrientationService getDeviceOrientationService() { - if (mDeviceOrientationService == null) { - mDeviceOrientationService = - new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext); - } - return mDeviceOrientationService; - } - - static void setShouldMonitorWebCoreThread() { - sShouldMonitorWebCoreThread = true; - } - - private native void nativePause(int nativeClass); - private native void nativeResume(int nativeClass); - private native void nativeFreeMemory(int nativeClass); - private native void nativeFullScreenPluginHidden(int nativeClass, int npp); - private native void nativePluginSurfaceReady(int nativeClass); - - private native WebKitHitTest nativeHitTest(int nativeClass, int x, int y, - int slop, boolean moveMouse); - - private native void nativeAutoFillForm(int nativeClass, int queryId); - private native void nativeScrollLayer(int nativeClass, int layer, Rect rect); - private native int nativeFindAll(int nativeClass, String text); - private native int nativeFindNext(int nativeClass, boolean forward); - - /** - * Deletes editable text between two points. Note that the selection may - * differ from the WebView's selection because the algorithms for selecting - * text differs for non-LTR text. Any text that isn't editable will be - * left unchanged. - * @param nativeClass The pointer to the native class (mNativeClass) - * @param startX The X position of the top-left selection point. - * @param startY The Y position of the top-left selection point. - * @param endX The X position of the bottom-right selection point. - * @param endY The Y position of the bottom-right selection point. - */ - private native void nativeDeleteText(int nativeClass, - int startX, int startY, int endX, int endY); - /** - * Inserts text at the current cursor position. If the currently-focused - * node does not have a cursor position then this function does nothing. - */ - private native void nativeInsertText(int nativeClass, String text); - /** - * Gets the text between two selection points. Note that the selection - * may differ from the WebView's selection because the algorithms for - * selecting text differs for non-LTR text. - * @param nativeClass The pointer to the native class (mNativeClass) - * @param startX The X position of the top-left selection point. - * @param startY The Y position of the top-left selection point. - * @param endX The X position of the bottom-right selection point. - * @param endY The Y position of the bottom-right selection point. - */ - private native String nativeGetText(int nativeClass, - int startX, int startY, int endX, int endY); - private native void nativeSelectText(int nativeClass, - int handleId, int x, int y); - private native void nativeClearTextSelection(int nativeClass); - private native boolean nativeSelectWordAt(int nativeClass, int x, int y); - private native void nativeSelectAll(int nativeClass); - private native void nativeSetInitialFocus(int nativeClass, int keyDirection); - - private static native void nativeCertTrustChanged(); -} diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java deleted file mode 100644 index be01028..0000000 --- a/core/java/android/webkit/WebViewDatabaseClassic.java +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteStatement; -import android.util.Log; - -final class WebViewDatabaseClassic extends WebViewDatabase { - private static final String LOGTAG = "WebViewDatabaseClassic"; - private static final String DATABASE_FILE = "webview.db"; - private static final String CACHE_DATABASE_FILE = "webviewCache.db"; - - private static final int DATABASE_VERSION = 11; - // 2 -> 3 Modified Cache table to allow cache of redirects - // 3 -> 4 Added Oma-Downloads table - // 4 -> 5 Modified Cache table to support persistent contentLength - // 5 -> 4 Removed Oma-Downoads table - // 5 -> 6 Add INDEX for cache table - // 6 -> 7 Change cache localPath from int to String - // 7 -> 8 Move cache to its own db - // 8 -> 9 Store both scheme and host when storing passwords - // 9 -> 10 Update httpauth table UNIQUE - // 10 -> 11 Drop cookies and cache now managed by the chromium stack, - // and update the form data table to use the new format - // implemented for b/5265606. - - private static WebViewDatabaseClassic sInstance = null; - private static final Object sInstanceLock = new Object(); - - private static SQLiteDatabase sDatabase = null; - - // synchronize locks - private final Object mPasswordLock = new Object(); - private final Object mFormLock = new Object(); - private final Object mHttpAuthLock = new Object(); - - private static final String mTableNames[] = { - "password", "formurl", "formdata", "httpauth" - }; - - // Table ids (they are index to mTableNames) - private static final int TABLE_PASSWORD_ID = 0; - private static final int TABLE_FORMURL_ID = 1; - private static final int TABLE_FORMDATA_ID = 2; - private static final int TABLE_HTTPAUTH_ID = 3; - - // column id strings for "_id" which can be used by any table - private static final String ID_COL = "_id"; - - private static final String[] ID_PROJECTION = new String[] { - "_id" - }; - - // column id strings for "password" table - private static final String PASSWORD_HOST_COL = "host"; - private static final String PASSWORD_USERNAME_COL = "username"; - private static final String PASSWORD_PASSWORD_COL = "password"; - - // column id strings for "formurl" table - private static final String FORMURL_URL_COL = "url"; - - // column id strings for "formdata" table - private static final String FORMDATA_URLID_COL = "urlid"; - private static final String FORMDATA_NAME_COL = "name"; - private static final String FORMDATA_VALUE_COL = "value"; - - // column id strings for "httpauth" table - private static final String HTTPAUTH_HOST_COL = "host"; - private static final String HTTPAUTH_REALM_COL = "realm"; - private static final String HTTPAUTH_USERNAME_COL = "username"; - private static final String HTTPAUTH_PASSWORD_COL = "password"; - - // Initially true until the background thread completes. - private boolean mInitialized = false; - - private WebViewDatabaseClassic(final Context context) { - JniUtil.setContext(context); - new Thread() { - @Override - public void run() { - init(context); - } - }.start(); - - // Singleton only, use getInstance() - } - - public static WebViewDatabaseClassic getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new WebViewDatabaseClassic(context); - } - return sInstance; - } - } - - private synchronized void init(Context context) { - if (mInitialized) { - return; - } - - initDatabase(context); - // Before using the Chromium HTTP stack, we stored the WebKit cache in - // our own DB. Clean up the DB file if it's still around. - context.deleteDatabase(CACHE_DATABASE_FILE); - - // Thread done, notify. - mInitialized = true; - notify(); - } - - private void initDatabase(Context context) { - try { - sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null); - } catch (SQLiteException e) { - // try again by deleting the old db and create a new one - if (context.deleteDatabase(DATABASE_FILE)) { - sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, - null); - } - } - - // sDatabase should not be null, - // the only case is RequestAPI test has problem to create db - if (sDatabase == null) { - mInitialized = true; - notify(); - return; - } - - if (sDatabase.getVersion() != DATABASE_VERSION) { - sDatabase.beginTransactionNonExclusive(); - try { - upgradeDatabase(); - sDatabase.setTransactionSuccessful(); - } finally { - sDatabase.endTransaction(); - } - } - } - - private static void upgradeDatabase() { - upgradeDatabaseToV10(); - upgradeDatabaseFromV10ToV11(); - // Add future database upgrade functions here, one version at a - // time. - sDatabase.setVersion(DATABASE_VERSION); - } - - private static void upgradeDatabaseFromV10ToV11() { - int oldVersion = sDatabase.getVersion(); - - if (oldVersion >= 11) { - // Nothing to do. - return; - } - - // Clear out old java stack cookies - this data is now stored in - // a separate database managed by the Chrome stack. - sDatabase.execSQL("DROP TABLE IF EXISTS cookies"); - - // Likewise for the old cache table. - sDatabase.execSQL("DROP TABLE IF EXISTS cache"); - - // Update form autocomplete URLs to match new ICS formatting. - Cursor c = sDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null, - null, null, null, null); - while (c.moveToNext()) { - String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL))); - String url = c.getString(c.getColumnIndex(FORMURL_URL_COL)); - ContentValues cv = new ContentValues(1); - cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url)); - sDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?", - new String[] { urlId }); - } - c.close(); - } - - private static void upgradeDatabaseToV10() { - int oldVersion = sDatabase.getVersion(); - - if (oldVersion >= 10) { - // Nothing to do. - return; - } - - if (oldVersion != 0) { - Log.i(LOGTAG, "Upgrading database from version " - + oldVersion + " to " - + DATABASE_VERSION + ", which will destroy old data"); - } - - if (9 == oldVersion) { - sDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_HTTPAUTH_ID]); - sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL - + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " - + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" - + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL - + ") ON CONFLICT REPLACE);"); - return; - } - - sDatabase.execSQL("DROP TABLE IF EXISTS cookies"); - sDatabase.execSQL("DROP TABLE IF EXISTS cache"); - sDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMURL_ID]); - sDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMDATA_ID]); - sDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_HTTPAUTH_ID]); - sDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_PASSWORD_ID]); - - // formurl - sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL - + " TEXT" + ");"); - - // formdata - sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL - + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" - + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " - + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); - - // httpauth - sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL - + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " - + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" - + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL - + ") ON CONFLICT REPLACE);"); - // passwords - sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL - + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE (" - + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL - + ") ON CONFLICT REPLACE);"); - } - - // Wait for the background initialization thread to complete and check the - // database creation status. - private boolean checkInitialized() { - synchronized (this) { - while (!mInitialized) { - try { - wait(); - } catch (InterruptedException e) { - Log.e(LOGTAG, "Caught exception while checking " + - "initialization"); - Log.e(LOGTAG, Log.getStackTraceString(e)); - } - } - } - return sDatabase != null; - } - - private boolean hasEntries(int tableId) { - if (!checkInitialized()) { - return false; - } - - Cursor cursor = null; - boolean ret = false; - try { - cursor = sDatabase.query(mTableNames[tableId], ID_PROJECTION, - null, null, null, null, null); - ret = cursor.moveToFirst() == true; - } catch (IllegalStateException e) { - Log.e(LOGTAG, "hasEntries", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - - // - // password functions - // - - /** - * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique. - * - * @param schemePlusHost The scheme and host for the password - * @param username The username for the password. If it is null, it means - * password can't be saved. - * @param password The password - */ - void setUsernamePassword(String schemePlusHost, String username, - String password) { - if (schemePlusHost == null || !checkInitialized()) { - return; - } - - synchronized (mPasswordLock) { - final ContentValues c = new ContentValues(); - c.put(PASSWORD_HOST_COL, schemePlusHost); - c.put(PASSWORD_USERNAME_COL, username); - c.put(PASSWORD_PASSWORD_COL, password); - sDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL, - c); - } - } - - /** - * Retrieve the username and password for a given host - * - * @param schemePlusHost The scheme and host which passwords applies to - * @return String[] if found, String[0] is username, which can be null and - * String[1] is password. Return null if it can't find anything. - */ - String[] getUsernamePassword(String schemePlusHost) { - if (schemePlusHost == null || !checkInitialized()) { - return null; - } - - final String[] columns = new String[] { - PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL - }; - final String selection = "(" + PASSWORD_HOST_COL + " == ?)"; - synchronized (mPasswordLock) { - String[] ret = null; - Cursor cursor = null; - try { - cursor = sDatabase.query(mTableNames[TABLE_PASSWORD_ID], - columns, selection, new String[] { schemePlusHost }, null, - null, null); - if (cursor.moveToFirst()) { - ret = new String[2]; - ret[0] = cursor.getString( - cursor.getColumnIndex(PASSWORD_USERNAME_COL)); - ret[1] = cursor.getString( - cursor.getColumnIndex(PASSWORD_PASSWORD_COL)); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getUsernamePassword", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - } - - /** - * @see WebViewDatabase#hasUsernamePassword - */ - @Override - public boolean hasUsernamePassword() { - synchronized (mPasswordLock) { - return hasEntries(TABLE_PASSWORD_ID); - } - } - - /** - * @see WebViewDatabase#clearUsernamePassword - */ - @Override - public void clearUsernamePassword() { - if (!checkInitialized()) { - return; - } - - synchronized (mPasswordLock) { - sDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null); - } - } - - // - // http authentication password functions - // - - /** - * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, - * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique. - * - * @param host The host for the password - * @param realm The realm for the password - * @param username The username for the password. If it is null, it means - * password can't be saved. - * @param password The password - */ - void setHttpAuthUsernamePassword(String host, String realm, String username, - String password) { - if (host == null || realm == null || !checkInitialized()) { - return; - } - - synchronized (mHttpAuthLock) { - final ContentValues c = new ContentValues(); - c.put(HTTPAUTH_HOST_COL, host); - c.put(HTTPAUTH_REALM_COL, realm); - c.put(HTTPAUTH_USERNAME_COL, username); - c.put(HTTPAUTH_PASSWORD_COL, password); - sDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL, - c); - } - } - - /** - * Retrieve the HTTP authentication username and password for a given - * host+realm pair - * - * @param host The host the password applies to - * @param realm The realm the password applies to - * @return String[] if found, String[0] is username, which can be null and - * String[1] is password. Return null if it can't find anything. - */ - String[] getHttpAuthUsernamePassword(String host, String realm) { - if (host == null || realm == null || !checkInitialized()){ - return null; - } - - final String[] columns = new String[] { - HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL - }; - final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND (" - + HTTPAUTH_REALM_COL + " == ?)"; - synchronized (mHttpAuthLock) { - String[] ret = null; - Cursor cursor = null; - try { - cursor = sDatabase.query(mTableNames[TABLE_HTTPAUTH_ID], - columns, selection, new String[] { host, realm }, null, - null, null); - if (cursor.moveToFirst()) { - ret = new String[2]; - ret[0] = cursor.getString( - cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)); - ret[1] = cursor.getString( - cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getHttpAuthUsernamePassword", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - } - - /** - * @see WebViewDatabase#hasHttpAuthUsernamePassword - */ - @Override - public boolean hasHttpAuthUsernamePassword() { - synchronized (mHttpAuthLock) { - return hasEntries(TABLE_HTTPAUTH_ID); - } - } - - /** - * @see WebViewDatabase#clearHttpAuthUsernamePassword - */ - @Override - public void clearHttpAuthUsernamePassword() { - if (!checkInitialized()) { - return; - } - - synchronized (mHttpAuthLock) { - sDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null); - } - } - - // - // form data functions - // - - /** - * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL, - * FORMDATA_VALUE_COL) is unique - * - * @param url The url of the site - * @param formdata The form data in HashMap - */ - void setFormData(String url, HashMap<String, String> formdata) { - if (url == null || formdata == null || !checkInitialized()) { - return; - } - - final String selection = "(" + FORMURL_URL_COL + " == ?)"; - synchronized (mFormLock) { - long urlid = -1; - Cursor cursor = null; - try { - cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID], - ID_PROJECTION, selection, new String[] { url }, null, null, - null); - if (cursor.moveToFirst()) { - urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); - } else { - ContentValues c = new ContentValues(); - c.put(FORMURL_URL_COL, url); - urlid = sDatabase.insert( - mTableNames[TABLE_FORMURL_ID], null, c); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "setFormData", e); - } finally { - if (cursor != null) cursor.close(); - } - if (urlid >= 0) { - Set<Entry<String, String>> set = formdata.entrySet(); - Iterator<Entry<String, String>> iter = set.iterator(); - ContentValues map = new ContentValues(); - map.put(FORMDATA_URLID_COL, urlid); - while (iter.hasNext()) { - Entry<String, String> entry = iter.next(); - map.put(FORMDATA_NAME_COL, entry.getKey()); - map.put(FORMDATA_VALUE_COL, entry.getValue()); - sDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map); - } - } - } - } - - /** - * Get all the values for a form entry with "name" in a given site - * - * @param url The url of the site - * @param name The name of the form entry - * @return A list of values. Return empty list if nothing is found. - */ - ArrayList<String> getFormData(String url, String name) { - ArrayList<String> values = new ArrayList<String>(); - if (url == null || name == null || !checkInitialized()) { - return values; - } - - final String urlSelection = "(" + FORMURL_URL_COL + " == ?)"; - final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND (" - + FORMDATA_NAME_COL + " == ?)"; - synchronized (mFormLock) { - Cursor cursor = null; - try { - cursor = sDatabase.query(mTableNames[TABLE_FORMURL_ID], - ID_PROJECTION, urlSelection, new String[] { url }, null, - null, null); - while (cursor.moveToNext()) { - long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); - Cursor dataCursor = null; - try { - dataCursor = sDatabase.query( - mTableNames[TABLE_FORMDATA_ID], - new String[] { ID_COL, FORMDATA_VALUE_COL }, - dataSelection, - new String[] { Long.toString(urlid), name }, - null, null, null); - if (dataCursor.moveToFirst()) { - int valueCol = dataCursor.getColumnIndex( - FORMDATA_VALUE_COL); - do { - values.add(dataCursor.getString(valueCol)); - } while (dataCursor.moveToNext()); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getFormData dataCursor", e); - } finally { - if (dataCursor != null) dataCursor.close(); - } - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getFormData cursor", e); - } finally { - if (cursor != null) cursor.close(); - } - return values; - } - } - - /** - * @see WebViewDatabase#hasFormData - */ - @Override - public boolean hasFormData() { - synchronized (mFormLock) { - return hasEntries(TABLE_FORMURL_ID); - } - } - - /** - * @see WebViewDatabase#clearFormData - */ - @Override - public void clearFormData() { - if (!checkInitialized()) { - return; - } - - synchronized (mFormLock) { - sDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null); - sDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null); - } - } -} diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java deleted file mode 100644 index f64547f..0000000 --- a/core/java/android/webkit/WebViewInputDispatcher.java +++ /dev/null @@ -1,1293 +0,0 @@ -/* - * Copyright (C) 2012 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 android.webkit; - -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.util.Log; -import android.view.MotionEvent; -import android.view.ViewConfiguration; - -/** - * Perform asynchronous dispatch of input events in a {@link WebView}. - * - * This dispatcher is shared by the UI thread ({@link WebViewClassic}) and web kit - * thread ({@link WebViewCore}). The UI thread enqueues events for - * processing, waits for the web kit thread to handle them, and then performs - * additional processing depending on the outcome. - * - * How it works: - * - * 1. The web view thread receives an input event from the input system on the UI - * thread in its {@link WebViewClassic#onTouchEvent} handler. It sends the input event - * to the dispatcher, then immediately returns true to the input system to indicate that - * it will handle the event. - * - * 2. The web kit thread is notified that an event has been enqueued. Meanwhile additional - * events may be enqueued from the UI thread. In some cases, the dispatcher may decide to - * coalesce motion events into larger batches or to cancel events that have been - * sitting in the queue for too long. - * - * 3. The web kit thread wakes up and handles all input events that are waiting for it. - * After processing each input event, it informs the dispatcher whether the web application - * has decided to handle the event itself and to prevent default event handling. - * - * 4. If web kit indicates that it wants to prevent default event handling, then web kit - * consumes the remainder of the gesture and web view receives a cancel event if - * needed. Otherwise, the web view handles the gesture on the UI thread normally. - * - * 5. If the web kit thread takes too long to handle an input event, then it loses the - * right to handle it. The dispatcher synthesizes a cancellation event for web kit and - * then tells the web view on the UI thread to handle the event that timed out along - * with the rest of the gesture. - * - * One thing to keep in mind about the dispatcher is that what goes into the dispatcher - * is not necessarily what the web kit or UI thread will see. As mentioned above, the - * dispatcher may tweak the input event stream to improve responsiveness. Both web view and - * web kit are guaranteed to perceive a consistent stream of input events but - * they might not always see the same events (especially if one decides - * to prevent the other from handling a particular gesture). - * - * This implementation very deliberately does not refer to the {@link WebViewClassic} - * or {@link WebViewCore} classes, preferring to communicate with them only via - * interfaces to avoid unintentional coupling to their implementation details. - * - * Currently, the input dispatcher only handles pointer events (includes touch, - * hover and scroll events). In principle, it could be extended to handle trackball - * and key events if needed. - * - * @hide - */ -final class WebViewInputDispatcher { - private static final String TAG = "WebViewInputDispatcher"; - private static final boolean DEBUG = false; - // This enables batching of MotionEvents. It will combine multiple MotionEvents - // together into a single MotionEvent if more events come in while we are - // still waiting on the processing of a previous event. - // If this is set to false, we will instead opt to drop ACTION_MOVE - // events we cannot keep up with. - // TODO: If batching proves to be working well, remove this - private static final boolean ENABLE_EVENT_BATCHING = true; - - private final Object mLock = new Object(); - - // Pool of queued input events. (guarded by mLock) - private static final int MAX_DISPATCH_EVENT_POOL_SIZE = 10; - private DispatchEvent mDispatchEventPool; - private int mDispatchEventPoolSize; - - // Posted state, tracks events posted to the dispatcher. (guarded by mLock) - private final TouchStream mPostTouchStream = new TouchStream(); - private boolean mPostSendTouchEventsToWebKit; - private boolean mPostDoNotSendTouchEventsToWebKitUntilNextGesture; - private boolean mPostLongPressScheduled; - private boolean mPostClickScheduled; - private boolean mPostShowTapHighlightScheduled; - private boolean mPostHideTapHighlightScheduled; - private int mPostLastWebKitXOffset; - private int mPostLastWebKitYOffset; - private float mPostLastWebKitScale; - - // State for event tracking (click, longpress, double tap, etc..) - private boolean mIsDoubleTapCandidate; - private boolean mIsTapCandidate; - private float mInitialDownX; - private float mInitialDownY; - private float mTouchSlopSquared; - private float mDoubleTapSlopSquared; - - // Web kit state, tracks events observed by web kit. (guarded by mLock) - private final DispatchEventQueue mWebKitDispatchEventQueue = new DispatchEventQueue(); - private final TouchStream mWebKitTouchStream = new TouchStream(); - private final WebKitCallbacks mWebKitCallbacks; - private final WebKitHandler mWebKitHandler; - private boolean mWebKitDispatchScheduled; - private boolean mWebKitTimeoutScheduled; - private long mWebKitTimeoutTime; - - // UI state, tracks events observed by the UI. (guarded by mLock) - private final DispatchEventQueue mUiDispatchEventQueue = new DispatchEventQueue(); - private final TouchStream mUiTouchStream = new TouchStream(); - private final UiCallbacks mUiCallbacks; - private final UiHandler mUiHandler; - private boolean mUiDispatchScheduled; - - // Give up on web kit handling of input events when this timeout expires. - private static final long WEBKIT_TIMEOUT_MILLIS = 200; - private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); - private static final int LONG_PRESS_TIMEOUT = - ViewConfiguration.getLongPressTimeout() + TAP_TIMEOUT; - private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); - private static final int PRESSED_STATE_DURATION = ViewConfiguration.getPressedStateDuration(); - - /** - * Event type: Indicates a touch event type. - * - * This event is delivered together with a {@link MotionEvent} with one of the - * following actions: {@link MotionEvent#ACTION_DOWN}, {@link MotionEvent#ACTION_MOVE}, - * {@link MotionEvent#ACTION_UP}, {@link MotionEvent#ACTION_POINTER_DOWN}, - * {@link MotionEvent#ACTION_POINTER_UP}, {@link MotionEvent#ACTION_CANCEL}. - */ - public static final int EVENT_TYPE_TOUCH = 0; - - /** - * Event type: Indicates a hover event type. - * - * This event is delivered together with a {@link MotionEvent} with one of the - * following actions: {@link MotionEvent#ACTION_HOVER_ENTER}, - * {@link MotionEvent#ACTION_HOVER_MOVE}, {@link MotionEvent#ACTION_HOVER_MOVE}. - */ - public static final int EVENT_TYPE_HOVER = 1; - - /** - * Event type: Indicates a scroll event type. - * - * This event is delivered together with a {@link MotionEvent} with action - * {@link MotionEvent#ACTION_SCROLL}. - */ - public static final int EVENT_TYPE_SCROLL = 2; - - /** - * Event type: Indicates a long-press event type. - * - * This event is delivered in the middle of a sequence of {@link #EVENT_TYPE_TOUCH} events. - * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_MOVE} - * that indicates the current touch coordinates of the long-press. - * - * This event is sent when the current touch gesture has been held longer than - * the long-press interval. - */ - public static final int EVENT_TYPE_LONG_PRESS = 3; - - /** - * Event type: Indicates a click event type. - * - * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that - * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}. - * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP} - * that indicates the location of the click. - * - * This event is sent shortly after the end of a touch after the double-tap - * interval has expired to indicate a click. - */ - public static final int EVENT_TYPE_CLICK = 4; - - /** - * Event type: Indicates a double-tap event type. - * - * This event is delivered after a sequence of {@link #EVENT_TYPE_TOUCH} events that - * comprise a complete gesture ending with {@link MotionEvent#ACTION_UP}. - * It includes a {@link MotionEvent} with action {@link MotionEvent#ACTION_UP} - * that indicates the location of the double-tap. - * - * This event is sent immediately after a sequence of two touches separated - * in time by no more than the double-tap interval and separated in space - * by no more than the double-tap slop. - */ - public static final int EVENT_TYPE_DOUBLE_TAP = 5; - - /** - * Event type: Indicates that a hit test should be performed - */ - public static final int EVENT_TYPE_HIT_TEST = 6; - - /** - * Flag: This event is private to this queue. Do not forward it. - */ - public static final int FLAG_PRIVATE = 1 << 0; - - /** - * Flag: This event is currently being processed by web kit. - * If a timeout occurs, make a copy of it before forwarding the event to another queue. - */ - public static final int FLAG_WEBKIT_IN_PROGRESS = 1 << 1; - - /** - * Flag: A timeout occurred while waiting for web kit to process this input event. - */ - public static final int FLAG_WEBKIT_TIMEOUT = 1 << 2; - - /** - * Flag: Indicates that the event was transformed for delivery to web kit. - * The event must be transformed back before being delivered to the UI. - */ - public static final int FLAG_WEBKIT_TRANSFORMED_EVENT = 1 << 3; - - public WebViewInputDispatcher(UiCallbacks uiCallbacks, WebKitCallbacks webKitCallbacks) { - this.mUiCallbacks = uiCallbacks; - mUiHandler = new UiHandler(uiCallbacks.getUiLooper()); - - this.mWebKitCallbacks = webKitCallbacks; - mWebKitHandler = new WebKitHandler(webKitCallbacks.getWebKitLooper()); - - ViewConfiguration config = ViewConfiguration.get(mUiCallbacks.getContext()); - mDoubleTapSlopSquared = config.getScaledDoubleTapSlop(); - mDoubleTapSlopSquared = (mDoubleTapSlopSquared * mDoubleTapSlopSquared); - mTouchSlopSquared = config.getScaledTouchSlop(); - mTouchSlopSquared = (mTouchSlopSquared * mTouchSlopSquared); - } - - /** - * Sets whether web kit wants to receive touch events. - * - * @param enable True to enable dispatching of touch events to web kit, otherwise - * web kit will be skipped. - */ - public void setWebKitWantsTouchEvents(boolean enable) { - if (DEBUG) { - Log.d(TAG, "webkitWantsTouchEvents: " + enable); - } - synchronized (mLock) { - if (mPostSendTouchEventsToWebKit != enable) { - if (!enable) { - enqueueWebKitCancelTouchEventIfNeededLocked(); - } - mPostSendTouchEventsToWebKit = enable; - } - } - } - - /** - * Posts a pointer event to the dispatch queue. - * - * @param event The event to post. - * @param webKitXOffset X offset to apply to events before dispatching them to web kit. - * @param webKitYOffset Y offset to apply to events before dispatching them to web kit. - * @param webKitScale The scale factor to apply to translated events before dispatching - * them to web kit. - * @return True if the dispatcher will handle the event, false if the event is unsupported. - */ - public boolean postPointerEvent(MotionEvent event, - int webKitXOffset, int webKitYOffset, float webKitScale) { - if (event == null) { - throw new IllegalArgumentException("event cannot be null"); - } - - if (DEBUG) { - Log.d(TAG, "postPointerEvent: " + event); - } - - final int action = event.getActionMasked(); - final int eventType; - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_CANCEL: - eventType = EVENT_TYPE_TOUCH; - break; - case MotionEvent.ACTION_SCROLL: - eventType = EVENT_TYPE_SCROLL; - break; - case MotionEvent.ACTION_HOVER_ENTER: - case MotionEvent.ACTION_HOVER_MOVE: - case MotionEvent.ACTION_HOVER_EXIT: - eventType = EVENT_TYPE_HOVER; - break; - default: - return false; // currently unsupported event type - } - - synchronized (mLock) { - // Ensure that the event is consistent and should be delivered. - MotionEvent eventToEnqueue = event; - if (eventType == EVENT_TYPE_TOUCH) { - eventToEnqueue = mPostTouchStream.update(event); - if (eventToEnqueue == null) { - if (DEBUG) { - Log.d(TAG, "postPointerEvent: dropped event " + event); - } - unscheduleLongPressLocked(); - unscheduleClickLocked(); - hideTapCandidateLocked(); - return false; - } - - if (action == MotionEvent.ACTION_DOWN && mPostSendTouchEventsToWebKit) { - if (mUiCallbacks.shouldInterceptTouchEvent(eventToEnqueue)) { - mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true; - } else if (mPostDoNotSendTouchEventsToWebKitUntilNextGesture) { - // Recover from a previous web kit timeout. - mPostDoNotSendTouchEventsToWebKitUntilNextGesture = false; - } - } - } - - // Copy the event because we need to retain ownership. - if (eventToEnqueue == event) { - eventToEnqueue = event.copy(); - } - - DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, eventType, 0, - webKitXOffset, webKitYOffset, webKitScale); - updateStateTrackersLocked(d, event); - enqueueEventLocked(d); - } - return true; - } - - private void scheduleLongPressLocked() { - unscheduleLongPressLocked(); - mPostLongPressScheduled = true; - mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_LONG_PRESS, - LONG_PRESS_TIMEOUT); - } - - private void unscheduleLongPressLocked() { - if (mPostLongPressScheduled) { - mPostLongPressScheduled = false; - mUiHandler.removeMessages(UiHandler.MSG_LONG_PRESS); - } - } - - private void postLongPress() { - synchronized (mLock) { - if (!mPostLongPressScheduled) { - return; - } - mPostLongPressScheduled = false; - - MotionEvent event = mPostTouchStream.getLastEvent(); - if (event == null) { - return; - } - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_POINTER_UP: - break; - default: - return; - } - - MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event); - eventToEnqueue.setAction(MotionEvent.ACTION_MOVE); - DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_LONG_PRESS, 0, - mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale); - enqueueEventLocked(d); - } - } - - private void hideTapCandidateLocked() { - unscheduleHideTapHighlightLocked(); - unscheduleShowTapHighlightLocked(); - mUiCallbacks.showTapHighlight(false); - } - - private void showTapCandidateLocked() { - unscheduleHideTapHighlightLocked(); - unscheduleShowTapHighlightLocked(); - mUiCallbacks.showTapHighlight(true); - } - - private void scheduleShowTapHighlightLocked() { - unscheduleShowTapHighlightLocked(); - mPostShowTapHighlightScheduled = true; - mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_SHOW_TAP_HIGHLIGHT, - TAP_TIMEOUT); - } - - private void unscheduleShowTapHighlightLocked() { - if (mPostShowTapHighlightScheduled) { - mPostShowTapHighlightScheduled = false; - mUiHandler.removeMessages(UiHandler.MSG_SHOW_TAP_HIGHLIGHT); - } - } - - private void scheduleHideTapHighlightLocked() { - unscheduleHideTapHighlightLocked(); - mPostHideTapHighlightScheduled = true; - mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_HIDE_TAP_HIGHLIGHT, - PRESSED_STATE_DURATION); - } - - private void unscheduleHideTapHighlightLocked() { - if (mPostHideTapHighlightScheduled) { - mPostHideTapHighlightScheduled = false; - mUiHandler.removeMessages(UiHandler.MSG_HIDE_TAP_HIGHLIGHT); - } - } - - private void postShowTapHighlight(boolean show) { - synchronized (mLock) { - if (show) { - if (!mPostShowTapHighlightScheduled) { - return; - } - mPostShowTapHighlightScheduled = false; - } else { - if (!mPostHideTapHighlightScheduled) { - return; - } - mPostHideTapHighlightScheduled = false; - } - mUiCallbacks.showTapHighlight(show); - } - } - - private void scheduleClickLocked() { - unscheduleClickLocked(); - mPostClickScheduled = true; - mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_CLICK, DOUBLE_TAP_TIMEOUT); - } - - private void unscheduleClickLocked() { - if (mPostClickScheduled) { - mPostClickScheduled = false; - mUiHandler.removeMessages(UiHandler.MSG_CLICK); - } - } - - private void postClick() { - synchronized (mLock) { - if (!mPostClickScheduled) { - return; - } - mPostClickScheduled = false; - - MotionEvent event = mPostTouchStream.getLastEvent(); - if (event == null || event.getAction() != MotionEvent.ACTION_UP) { - return; - } - - showTapCandidateLocked(); - MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event); - DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_CLICK, 0, - mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale); - enqueueEventLocked(d); - } - } - - private void checkForDoubleTapOnDownLocked(MotionEvent event) { - mIsDoubleTapCandidate = false; - if (!mPostClickScheduled) { - return; - } - int deltaX = (int) mInitialDownX - (int) event.getX(); - int deltaY = (int) mInitialDownY - (int) event.getY(); - if ((deltaX * deltaX + deltaY * deltaY) < mDoubleTapSlopSquared) { - unscheduleClickLocked(); - mIsDoubleTapCandidate = true; - } - } - - private boolean isClickCandidateLocked(MotionEvent event) { - if (event == null - || event.getActionMasked() != MotionEvent.ACTION_UP - || !mIsTapCandidate) { - return false; - } - long downDuration = event.getEventTime() - event.getDownTime(); - return downDuration < LONG_PRESS_TIMEOUT; - } - - private void enqueueDoubleTapLocked(MotionEvent event) { - MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event); - DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_DOUBLE_TAP, 0, - mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale); - enqueueEventLocked(d); - } - - private void enqueueHitTestLocked(MotionEvent event) { - mUiCallbacks.clearPreviousHitTest(); - MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event); - DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0, - mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale); - enqueueEventLocked(d); - } - - private void checkForSlopLocked(MotionEvent event) { - if (!mIsTapCandidate) { - return; - } - int deltaX = (int) mInitialDownX - (int) event.getX(); - int deltaY = (int) mInitialDownY - (int) event.getY(); - if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquared) { - unscheduleLongPressLocked(); - mIsTapCandidate = false; - hideTapCandidateLocked(); - } - } - - private void updateStateTrackersLocked(DispatchEvent d, MotionEvent event) { - mPostLastWebKitXOffset = d.mWebKitXOffset; - mPostLastWebKitYOffset = d.mWebKitYOffset; - mPostLastWebKitScale = d.mWebKitScale; - int action = event != null ? event.getAction() : MotionEvent.ACTION_CANCEL; - if (d.mEventType != EVENT_TYPE_TOUCH) { - return; - } - - if (action == MotionEvent.ACTION_CANCEL - || event.getPointerCount() > 1) { - unscheduleLongPressLocked(); - unscheduleClickLocked(); - hideTapCandidateLocked(); - mIsDoubleTapCandidate = false; - mIsTapCandidate = false; - hideTapCandidateLocked(); - } else if (action == MotionEvent.ACTION_DOWN) { - checkForDoubleTapOnDownLocked(event); - scheduleLongPressLocked(); - mIsTapCandidate = true; - mInitialDownX = event.getX(); - mInitialDownY = event.getY(); - enqueueHitTestLocked(event); - if (mIsDoubleTapCandidate) { - hideTapCandidateLocked(); - } else { - scheduleShowTapHighlightLocked(); - } - } else if (action == MotionEvent.ACTION_UP) { - unscheduleLongPressLocked(); - if (isClickCandidateLocked(event)) { - if (mIsDoubleTapCandidate) { - hideTapCandidateLocked(); - enqueueDoubleTapLocked(event); - } else { - scheduleClickLocked(); - } - } else { - hideTapCandidateLocked(); - } - } else if (action == MotionEvent.ACTION_MOVE) { - checkForSlopLocked(event); - } - } - - /** - * Dispatches pending web kit events. - * Must only be called from the web kit thread. - * - * This method may be used to flush the queue of pending input events - * immediately. This method may help to reduce input dispatch latency - * if called before certain expensive operations such as drawing. - */ - public void dispatchWebKitEvents() { - dispatchWebKitEvents(false); - } - - private void dispatchWebKitEvents(boolean calledFromHandler) { - for (;;) { - // Get the next event, but leave it in the queue so we can move it to the UI - // queue if a timeout occurs. - DispatchEvent d; - MotionEvent event; - final int eventType; - int flags; - synchronized (mLock) { - if (!ENABLE_EVENT_BATCHING) { - drainStaleWebKitEventsLocked(); - } - d = mWebKitDispatchEventQueue.mHead; - if (d == null) { - if (mWebKitDispatchScheduled) { - mWebKitDispatchScheduled = false; - if (!calledFromHandler) { - mWebKitHandler.removeMessages( - WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS); - } - } - return; - } - - event = d.mEvent; - if (event != null) { - event.offsetLocation(d.mWebKitXOffset, d.mWebKitYOffset); - event.scale(d.mWebKitScale); - d.mFlags |= FLAG_WEBKIT_TRANSFORMED_EVENT; - } - - eventType = d.mEventType; - if (eventType == EVENT_TYPE_TOUCH) { - event = mWebKitTouchStream.update(event); - if (DEBUG && event == null && d.mEvent != null) { - Log.d(TAG, "dispatchWebKitEvents: dropped event " + d.mEvent); - } - } - - d.mFlags |= FLAG_WEBKIT_IN_PROGRESS; - flags = d.mFlags; - } - - // Handle the event. - final boolean preventDefault; - if (event == null) { - preventDefault = false; - } else { - preventDefault = dispatchWebKitEvent(event, eventType, flags); - } - - synchronized (mLock) { - flags = d.mFlags; - d.mFlags = flags & ~FLAG_WEBKIT_IN_PROGRESS; - boolean recycleEvent = event != d.mEvent; - - if ((flags & FLAG_WEBKIT_TIMEOUT) != 0) { - // A timeout occurred! - recycleDispatchEventLocked(d); - } else { - // Web kit finished in a timely manner. Dequeue the event. - assert mWebKitDispatchEventQueue.mHead == d; - mWebKitDispatchEventQueue.dequeue(); - - updateWebKitTimeoutLocked(); - - if ((flags & FLAG_PRIVATE) != 0) { - // Event was intended for web kit only. All done. - recycleDispatchEventLocked(d); - } else if (preventDefault) { - // Web kit has decided to consume the event! - if (d.mEventType == EVENT_TYPE_TOUCH) { - enqueueUiCancelTouchEventIfNeededLocked(); - unscheduleLongPressLocked(); - } - } else { - // Web kit is being friendly. Pass the event to the UI. - enqueueUiEventUnbatchedLocked(d); - } - } - - if (event != null && recycleEvent) { - event.recycle(); - } - - if (eventType == EVENT_TYPE_CLICK) { - scheduleHideTapHighlightLocked(); - } - } - } - } - - // Runs on web kit thread. - private boolean dispatchWebKitEvent(MotionEvent event, int eventType, int flags) { - if (DEBUG) { - Log.d(TAG, "dispatchWebKitEvent: event=" + event - + ", eventType=" + eventType + ", flags=" + flags); - } - boolean preventDefault = mWebKitCallbacks.dispatchWebKitEvent( - this, event, eventType, flags); - if (DEBUG) { - Log.d(TAG, "dispatchWebKitEvent: preventDefault=" + preventDefault); - } - return preventDefault; - } - - private boolean isMoveEventLocked(DispatchEvent d) { - return d.mEvent != null - && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE; - } - - private void drainStaleWebKitEventsLocked() { - DispatchEvent d = mWebKitDispatchEventQueue.mHead; - while (d != null && d.mNext != null - && isMoveEventLocked(d) - && isMoveEventLocked(d.mNext)) { - DispatchEvent next = d.mNext; - skipWebKitEventLocked(d); - d = next; - } - mWebKitDispatchEventQueue.mHead = d; - } - - // Called by WebKit when it doesn't care about the rest of the touch stream - public void skipWebkitForRemainingTouchStream() { - // Just treat this like a timeout - handleWebKitTimeout(); - } - - // Runs on UI thread in response to the web kit thread appearing to be unresponsive. - private void handleWebKitTimeout() { - synchronized (mLock) { - if (!mWebKitTimeoutScheduled) { - return; - } - mWebKitTimeoutScheduled = false; - - if (DEBUG) { - Log.d(TAG, "handleWebKitTimeout: timeout occurred!"); - } - - // Drain the web kit event queue. - DispatchEvent d = mWebKitDispatchEventQueue.dequeueList(); - - // If web kit was processing an event (must be at the head of the list because - // it can only do one at a time), then clone it or ignore it. - if ((d.mFlags & FLAG_WEBKIT_IN_PROGRESS) != 0) { - d.mFlags |= FLAG_WEBKIT_TIMEOUT; - if ((d.mFlags & FLAG_PRIVATE) != 0) { - d = d.mNext; // the event is private to web kit, ignore it - } else { - d = copyDispatchEventLocked(d); - d.mFlags &= ~FLAG_WEBKIT_IN_PROGRESS; - } - } - - // Enqueue all non-private events for handling by the UI thread. - while (d != null) { - DispatchEvent next = d.mNext; - skipWebKitEventLocked(d); - d = next; - } - - // Tell web kit to cancel all pending touches. - // This also prevents us from sending web kit any more touches until the - // next gesture begins. (As required to ensure touch event stream consistency.) - enqueueWebKitCancelTouchEventIfNeededLocked(); - } - } - - private void skipWebKitEventLocked(DispatchEvent d) { - d.mNext = null; - if ((d.mFlags & FLAG_PRIVATE) != 0) { - recycleDispatchEventLocked(d); - } else { - d.mFlags |= FLAG_WEBKIT_TIMEOUT; - enqueueUiEventUnbatchedLocked(d); - } - } - - /** - * Dispatches pending UI events. - * Must only be called from the UI thread. - * - * This method may be used to flush the queue of pending input events - * immediately. This method may help to reduce input dispatch latency - * if called before certain expensive operations such as drawing. - */ - public void dispatchUiEvents() { - dispatchUiEvents(false); - } - - private void dispatchUiEvents(boolean calledFromHandler) { - for (;;) { - MotionEvent event; - final int eventType; - final int flags; - synchronized (mLock) { - DispatchEvent d = mUiDispatchEventQueue.dequeue(); - if (d == null) { - if (mUiDispatchScheduled) { - mUiDispatchScheduled = false; - if (!calledFromHandler) { - mUiHandler.removeMessages(UiHandler.MSG_DISPATCH_UI_EVENTS); - } - } - return; - } - - event = d.mEvent; - if (event != null && (d.mFlags & FLAG_WEBKIT_TRANSFORMED_EVENT) != 0) { - event.scale(1.0f / d.mWebKitScale); - event.offsetLocation(-d.mWebKitXOffset, -d.mWebKitYOffset); - d.mFlags &= ~FLAG_WEBKIT_TRANSFORMED_EVENT; - } - - eventType = d.mEventType; - if (eventType == EVENT_TYPE_TOUCH) { - event = mUiTouchStream.update(event); - if (DEBUG && event == null && d.mEvent != null) { - Log.d(TAG, "dispatchUiEvents: dropped event " + d.mEvent); - } - } - - flags = d.mFlags; - - if (event == d.mEvent) { - d.mEvent = null; // retain ownership of event, don't recycle it yet - } - recycleDispatchEventLocked(d); - - if (eventType == EVENT_TYPE_CLICK) { - scheduleHideTapHighlightLocked(); - } - } - - // Handle the event. - if (event != null) { - dispatchUiEvent(event, eventType, flags); - event.recycle(); - } - } - } - - // Runs on UI thread. - private void dispatchUiEvent(MotionEvent event, int eventType, int flags) { - if (DEBUG) { - Log.d(TAG, "dispatchUiEvent: event=" + event - + ", eventType=" + eventType + ", flags=" + flags); - } - mUiCallbacks.dispatchUiEvent(event, eventType, flags); - } - - private void enqueueEventLocked(DispatchEvent d) { - if (!shouldSkipWebKit(d)) { - enqueueWebKitEventLocked(d); - } else { - enqueueUiEventLocked(d); - } - } - - private boolean shouldSkipWebKit(DispatchEvent d) { - switch (d.mEventType) { - case EVENT_TYPE_CLICK: - case EVENT_TYPE_HOVER: - case EVENT_TYPE_SCROLL: - case EVENT_TYPE_HIT_TEST: - return false; - case EVENT_TYPE_TOUCH: - // TODO: This should be cleaned up. We now have WebViewInputDispatcher - // and WebViewClassic both checking for slop and doing their own - // thing - they should be consolidated. And by consolidated, I mean - // WebViewClassic's version should just be deleted. - // The reason this is done is because webpages seem to expect - // that they only get an ontouchmove if the slop has been exceeded. - if (mIsTapCandidate && d.mEvent != null - && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE) { - return true; - } - return !mPostSendTouchEventsToWebKit - || mPostDoNotSendTouchEventsToWebKitUntilNextGesture; - } - return true; - } - - private void enqueueWebKitCancelTouchEventIfNeededLocked() { - // We want to cancel touch events that were delivered to web kit. - // Enqueue a null event at the end of the queue if needed. - if (mWebKitTouchStream.isCancelNeeded() || !mWebKitDispatchEventQueue.isEmpty()) { - DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE, - 0, 0, 1.0f); - enqueueWebKitEventUnbatchedLocked(d); - mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true; - } - } - - private void enqueueWebKitEventLocked(DispatchEvent d) { - if (batchEventLocked(d, mWebKitDispatchEventQueue.mTail)) { - if (DEBUG) { - Log.d(TAG, "enqueueWebKitEventLocked: batched event " + d.mEvent); - } - recycleDispatchEventLocked(d); - } else { - enqueueWebKitEventUnbatchedLocked(d); - } - } - - private void enqueueWebKitEventUnbatchedLocked(DispatchEvent d) { - if (DEBUG) { - Log.d(TAG, "enqueueWebKitEventUnbatchedLocked: enqueued event " + d.mEvent); - } - mWebKitDispatchEventQueue.enqueue(d); - scheduleWebKitDispatchLocked(); - updateWebKitTimeoutLocked(); - } - - private void scheduleWebKitDispatchLocked() { - if (!mWebKitDispatchScheduled) { - mWebKitHandler.sendEmptyMessage(WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS); - mWebKitDispatchScheduled = true; - } - } - - private void updateWebKitTimeoutLocked() { - DispatchEvent d = mWebKitDispatchEventQueue.mHead; - if (d != null && mWebKitTimeoutScheduled && mWebKitTimeoutTime == d.mTimeoutTime) { - return; - } - if (mWebKitTimeoutScheduled) { - mUiHandler.removeMessages(UiHandler.MSG_WEBKIT_TIMEOUT); - mWebKitTimeoutScheduled = false; - } - if (d != null) { - mUiHandler.sendEmptyMessageAtTime(UiHandler.MSG_WEBKIT_TIMEOUT, d.mTimeoutTime); - mWebKitTimeoutScheduled = true; - mWebKitTimeoutTime = d.mTimeoutTime; - } - } - - private void enqueueUiCancelTouchEventIfNeededLocked() { - // We want to cancel touch events that were delivered to the UI. - // Enqueue a null event at the end of the queue if needed. - if (mUiTouchStream.isCancelNeeded() || !mUiDispatchEventQueue.isEmpty()) { - DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE, - 0, 0, 1.0f); - enqueueUiEventUnbatchedLocked(d); - } - } - - private void enqueueUiEventLocked(DispatchEvent d) { - if (batchEventLocked(d, mUiDispatchEventQueue.mTail)) { - if (DEBUG) { - Log.d(TAG, "enqueueUiEventLocked: batched event " + d.mEvent); - } - recycleDispatchEventLocked(d); - } else { - enqueueUiEventUnbatchedLocked(d); - } - } - - private void enqueueUiEventUnbatchedLocked(DispatchEvent d) { - if (DEBUG) { - Log.d(TAG, "enqueueUiEventUnbatchedLocked: enqueued event " + d.mEvent); - } - mUiDispatchEventQueue.enqueue(d); - scheduleUiDispatchLocked(); - } - - private void scheduleUiDispatchLocked() { - if (!mUiDispatchScheduled) { - mUiHandler.sendEmptyMessage(UiHandler.MSG_DISPATCH_UI_EVENTS); - mUiDispatchScheduled = true; - } - } - - private boolean batchEventLocked(DispatchEvent in, DispatchEvent tail) { - if (!ENABLE_EVENT_BATCHING) { - return false; - } - if (tail != null && tail.mEvent != null && in.mEvent != null - && in.mEventType == tail.mEventType - && in.mFlags == tail.mFlags - && in.mWebKitXOffset == tail.mWebKitXOffset - && in.mWebKitYOffset == tail.mWebKitYOffset - && in.mWebKitScale == tail.mWebKitScale) { - return tail.mEvent.addBatch(in.mEvent); - } - return false; - } - - private DispatchEvent obtainDispatchEventLocked(MotionEvent event, - int eventType, int flags, int webKitXOffset, int webKitYOffset, float webKitScale) { - DispatchEvent d = obtainUninitializedDispatchEventLocked(); - d.mEvent = event; - d.mEventType = eventType; - d.mFlags = flags; - d.mTimeoutTime = SystemClock.uptimeMillis() + WEBKIT_TIMEOUT_MILLIS; - d.mWebKitXOffset = webKitXOffset; - d.mWebKitYOffset = webKitYOffset; - d.mWebKitScale = webKitScale; - if (DEBUG) { - Log.d(TAG, "Timeout time: " + (d.mTimeoutTime - SystemClock.uptimeMillis())); - } - return d; - } - - private DispatchEvent copyDispatchEventLocked(DispatchEvent d) { - DispatchEvent copy = obtainUninitializedDispatchEventLocked(); - if (d.mEvent != null) { - copy.mEvent = d.mEvent.copy(); - } - copy.mEventType = d.mEventType; - copy.mFlags = d.mFlags; - copy.mTimeoutTime = d.mTimeoutTime; - copy.mWebKitXOffset = d.mWebKitXOffset; - copy.mWebKitYOffset = d.mWebKitYOffset; - copy.mWebKitScale = d.mWebKitScale; - copy.mNext = d.mNext; - return copy; - } - - private DispatchEvent obtainUninitializedDispatchEventLocked() { - DispatchEvent d = mDispatchEventPool; - if (d != null) { - mDispatchEventPoolSize -= 1; - mDispatchEventPool = d.mNext; - d.mNext = null; - } else { - d = new DispatchEvent(); - } - return d; - } - - private void recycleDispatchEventLocked(DispatchEvent d) { - if (d.mEvent != null) { - d.mEvent.recycle(); - d.mEvent = null; - } - - if (mDispatchEventPoolSize < MAX_DISPATCH_EVENT_POOL_SIZE) { - mDispatchEventPoolSize += 1; - d.mNext = mDispatchEventPool; - mDispatchEventPool = d; - } - } - - /* Implemented by {@link WebViewClassic} to perform operations on the UI thread. */ - public static interface UiCallbacks { - /** - * Gets the UI thread's looper. - * @return The looper. - */ - public Looper getUiLooper(); - - /** - * Gets the UI's context - * @return The context - */ - public Context getContext(); - - /** - * Dispatches an event to the UI. - * @param event The event. - * @param eventType The event type. - * @param flags The event's dispatch flags. - */ - public void dispatchUiEvent(MotionEvent event, int eventType, int flags); - - /** - * Asks the UI thread whether this touch event stream should be - * intercepted based on the touch down event. - * @param event The touch down event. - * @return true if the UI stream wants the touch stream without going - * through webkit or false otherwise. - */ - public boolean shouldInterceptTouchEvent(MotionEvent event); - - /** - * Inform's the UI that it should show the tap highlight - * @param show True if it should show the highlight, false if it should hide it - */ - public void showTapHighlight(boolean show); - - /** - * Called when we are sending a new EVENT_TYPE_HIT_TEST to WebKit, so - * previous hit tests should be cleared as they are obsolete. - */ - public void clearPreviousHitTest(); - } - - /* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */ - public static interface WebKitCallbacks { - /** - * Gets the web kit thread's looper. - * @return The looper. - */ - public Looper getWebKitLooper(); - - /** - * Dispatches an event to web kit. - * @param dispatcher The WebViewInputDispatcher sending the event - * @param event The event. - * @param eventType The event type. - * @param flags The event's dispatch flags. - * @return True if web kit wants to prevent default event handling. - */ - public boolean dispatchWebKitEvent(WebViewInputDispatcher dispatcher, - MotionEvent event, int eventType, int flags); - } - - // Runs on UI thread. - private final class UiHandler extends Handler { - public static final int MSG_DISPATCH_UI_EVENTS = 1; - public static final int MSG_WEBKIT_TIMEOUT = 2; - public static final int MSG_LONG_PRESS = 3; - public static final int MSG_CLICK = 4; - public static final int MSG_SHOW_TAP_HIGHLIGHT = 5; - public static final int MSG_HIDE_TAP_HIGHLIGHT = 6; - - public UiHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_DISPATCH_UI_EVENTS: - dispatchUiEvents(true); - break; - case MSG_WEBKIT_TIMEOUT: - handleWebKitTimeout(); - break; - case MSG_LONG_PRESS: - postLongPress(); - break; - case MSG_CLICK: - postClick(); - break; - case MSG_SHOW_TAP_HIGHLIGHT: - postShowTapHighlight(true); - break; - case MSG_HIDE_TAP_HIGHLIGHT: - postShowTapHighlight(false); - break; - default: - throw new IllegalStateException("Unknown message type: " + msg.what); - } - } - } - - // Runs on web kit thread. - private final class WebKitHandler extends Handler { - public static final int MSG_DISPATCH_WEBKIT_EVENTS = 1; - - public WebKitHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_DISPATCH_WEBKIT_EVENTS: - dispatchWebKitEvents(true); - break; - default: - throw new IllegalStateException("Unknown message type: " + msg.what); - } - } - } - - private static final class DispatchEvent { - public DispatchEvent mNext; - - public MotionEvent mEvent; - public int mEventType; - public int mFlags; - public long mTimeoutTime; - public int mWebKitXOffset; - public int mWebKitYOffset; - public float mWebKitScale; - } - - private static final class DispatchEventQueue { - public DispatchEvent mHead; - public DispatchEvent mTail; - - public boolean isEmpty() { - return mHead != null; - } - - public void enqueue(DispatchEvent d) { - if (mHead == null) { - mHead = d; - mTail = d; - } else { - mTail.mNext = d; - mTail = d; - } - } - - public DispatchEvent dequeue() { - DispatchEvent d = mHead; - if (d != null) { - DispatchEvent next = d.mNext; - if (next == null) { - mHead = null; - mTail = null; - } else { - mHead = next; - d.mNext = null; - } - } - return d; - } - - public DispatchEvent dequeueList() { - DispatchEvent d = mHead; - if (d != null) { - mHead = null; - mTail = null; - } - return d; - } - } - - /** - * Keeps track of a stream of touch events so that we can discard touch - * events that would make the stream inconsistent. - */ - private static final class TouchStream { - private MotionEvent mLastEvent; - - /** - * Gets the last touch event that was delivered. - * @return The last touch event, or null if none. - */ - public MotionEvent getLastEvent() { - return mLastEvent; - } - - /** - * Updates the touch event stream. - * @param event The event that we intend to send, or null to cancel the - * touch event stream. - * @return The event that we should actually send, or null if no event should - * be sent because the proposed event would make the stream inconsistent. - */ - public MotionEvent update(MotionEvent event) { - if (event == null) { - if (isCancelNeeded()) { - event = mLastEvent; - if (event != null) { - event.setAction(MotionEvent.ACTION_CANCEL); - mLastEvent = null; - } - } - return event; - } - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_MOVE: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_POINTER_UP: - if (mLastEvent == null - || mLastEvent.getAction() == MotionEvent.ACTION_UP) { - return null; - } - updateLastEvent(event); - return event; - - case MotionEvent.ACTION_DOWN: - updateLastEvent(event); - return event; - - case MotionEvent.ACTION_CANCEL: - if (mLastEvent == null) { - return null; - } - updateLastEvent(null); - return event; - - default: - return null; - } - } - - /** - * Returns true if there is a gesture in progress that may need to be canceled. - * @return True if cancel is needed. - */ - public boolean isCancelNeeded() { - return mLastEvent != null && mLastEvent.getAction() != MotionEvent.ACTION_UP; - } - - private void updateLastEvent(MotionEvent event) { - if (mLastEvent != null) { - mLastEvent.recycle(); - } - mLastEvent = event != null ? MotionEvent.obtainNoHistory(event) : null; - } - } -}
\ No newline at end of file diff --git a/core/java/android/webkit/ZoomControlBase.java b/core/java/android/webkit/ZoomControlBase.java deleted file mode 100644 index be9e8f3..0000000 --- a/core/java/android/webkit/ZoomControlBase.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -interface ZoomControlBase { - - /** - * Causes the on-screen zoom control to be made visible - */ - public void show(); - - /** - * Causes the on-screen zoom control to disappear - */ - public void hide(); - - /** - * Enables the control to update its state if necessary in response to a - * change in the pages zoom level. For example, if the max zoom level is - * reached then the control can disable the button for zooming in. - */ - public void update(); - - /** - * Checks to see if the control is currently visible to the user. - */ - public boolean isVisible(); -} diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java deleted file mode 100644 index ae19832..0000000 --- a/core/java/android/webkit/ZoomControlEmbedded.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.Toast; -import android.widget.ZoomButtonsController; - -class ZoomControlEmbedded implements ZoomControlBase { - - private final ZoomManager mZoomManager; - private final WebViewClassic mWebView; - - // The controller is lazily initialized in getControls() for performance. - private ZoomButtonsController mZoomButtonsController; - - public ZoomControlEmbedded(ZoomManager zoomManager, WebViewClassic webView) { - mZoomManager = zoomManager; - mWebView = webView; - } - - public void show() { - if (!getControls().isVisible() && !mZoomManager.isZoomScaleFixed()) { - - mZoomButtonsController.setVisible(true); - - if (mZoomManager.isDoubleTapEnabled()) { - WebSettingsClassic settings = mWebView.getSettings(); - int count = settings.getDoubleTapToastCount(); - if (mZoomManager.isInZoomOverview() && count > 0) { - settings.setDoubleTapToastCount(--count); - Toast.makeText(mWebView.getContext(), - com.android.internal.R.string.double_tap_toast, - Toast.LENGTH_LONG).show(); - } - } - } - } - - public void hide() { - if (mZoomButtonsController != null) { - mZoomButtonsController.setVisible(false); - } - } - - public boolean isVisible() { - return mZoomButtonsController != null && mZoomButtonsController.isVisible(); - } - - public void update() { - if (mZoomButtonsController == null) { - return; - } - - boolean canZoomIn = mZoomManager.canZoomIn(); - boolean canZoomOut = mZoomManager.canZoomOut() && !mZoomManager.isInZoomOverview(); - if (!canZoomIn && !canZoomOut) { - // Hide the zoom in and out buttons if the page cannot zoom - mZoomButtonsController.getZoomControls().setVisibility(View.GONE); - } else { - // Set each one individually, as a page may be able to zoom in or out - mZoomButtonsController.setZoomInEnabled(canZoomIn); - mZoomButtonsController.setZoomOutEnabled(canZoomOut); - } - } - - private ZoomButtonsController getControls() { - if (mZoomButtonsController == null) { - mZoomButtonsController = new ZoomButtonsController(mWebView.getWebView()); - mZoomButtonsController.setOnZoomListener(new ZoomListener()); - // ZoomButtonsController positions the buttons at the bottom, but in - // the middle. Change their layout parameters so they appear on the - // right. - View controls = mZoomButtonsController.getZoomControls(); - ViewGroup.LayoutParams params = controls.getLayoutParams(); - if (params instanceof FrameLayout.LayoutParams) { - ((FrameLayout.LayoutParams) params).gravity = Gravity.END; - } - } - return mZoomButtonsController; - } - - private class ZoomListener implements ZoomButtonsController.OnZoomListener { - - public void onVisibilityChanged(boolean visible) { - if (visible) { - mWebView.switchOutDrawHistory(); - // Bring back the hidden zoom controls. - mZoomButtonsController.getZoomControls().setVisibility(View.VISIBLE); - update(); - } - } - - public void onZoom(boolean zoomIn) { - if (zoomIn) { - mWebView.zoomIn(); - } else { - mWebView.zoomOut(); - } - update(); - } - } -} diff --git a/core/java/android/webkit/ZoomControlExternal.java b/core/java/android/webkit/ZoomControlExternal.java deleted file mode 100644 index f5bfc05..0000000 --- a/core/java/android/webkit/ZoomControlExternal.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; -import android.os.Handler; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.View.OnClickListener; -import android.view.animation.AlphaAnimation; -import android.widget.FrameLayout; - -@Deprecated -class ZoomControlExternal implements ZoomControlBase { - - // The time that the external controls are visible before fading away - private static final long ZOOM_CONTROLS_TIMEOUT = - ViewConfiguration.getZoomControlsTimeout(); - // The view containing the external zoom controls - private ExtendedZoomControls mZoomControls; - private Runnable mZoomControlRunnable; - private final Handler mPrivateHandler = new Handler(); - - private final WebViewClassic mWebView; - - public ZoomControlExternal(WebViewClassic webView) { - mWebView = webView; - } - - public void show() { - if(mZoomControlRunnable != null) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } - getControls().show(true); - mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT); - } - - public void hide() { - if (mZoomControlRunnable != null) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } - if (mZoomControls != null) { - mZoomControls.hide(); - } - } - - public boolean isVisible() { - return mZoomControls != null && mZoomControls.isShown(); - } - - public void update() { } - - public ExtendedZoomControls getControls() { - if (mZoomControls == null) { - mZoomControls = createZoomControls(); - - /* - * need to be set to VISIBLE first so that getMeasuredHeight() in - * {@link #onSizeChanged()} can return the measured value for proper - * layout. - */ - mZoomControls.setVisibility(View.VISIBLE); - mZoomControlRunnable = new Runnable() { - public void run() { - /* Don't dismiss the controls if the user has - * focus on them. Wait and check again later. - */ - if (!mZoomControls.hasFocus()) { - mZoomControls.hide(); - } else { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - } - } - }; - } - return mZoomControls; - } - - private ExtendedZoomControls createZoomControls() { - ExtendedZoomControls zoomControls = new ExtendedZoomControls(mWebView.getContext()); - zoomControls.setOnZoomInClickListener(new OnClickListener() { - public void onClick(View v) { - // reset time out - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT); - mWebView.zoomIn(); - } - }); - zoomControls.setOnZoomOutClickListener(new OnClickListener() { - public void onClick(View v) { - // reset time out - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT); - mWebView.zoomOut(); - } - }); - return zoomControls; - } - - private static class ExtendedZoomControls extends FrameLayout { - - private android.widget.ZoomControls mPlusMinusZoomControls; - - public ExtendedZoomControls(Context context) { - super(context, null); - LayoutInflater inflater = (LayoutInflater) - context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true); - mPlusMinusZoomControls = (android.widget.ZoomControls) findViewById( - com.android.internal.R.id.zoomControls); - findViewById(com.android.internal.R.id.zoomMagnify).setVisibility( - View.GONE); - } - - public void show(boolean showZoom) { - mPlusMinusZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE); - fade(View.VISIBLE, 0.0f, 1.0f); - } - - public void hide() { - fade(View.GONE, 1.0f, 0.0f); - } - - private void fade(int visibility, float startAlpha, float endAlpha) { - AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha); - anim.setDuration(500); - startAnimation(anim); - setVisibility(visibility); - } - - public boolean hasFocus() { - return mPlusMinusZoomControls.hasFocus(); - } - - public void setOnZoomInClickListener(OnClickListener listener) { - mPlusMinusZoomControls.setOnZoomInClickListener(listener); - } - - public void setOnZoomOutClickListener(OnClickListener listener) { - mPlusMinusZoomControls.setOnZoomOutClickListener(listener); - } - } -} diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java deleted file mode 100644 index 1d864e5..0000000 --- a/core/java/android/webkit/ZoomManager.java +++ /dev/null @@ -1,1263 +0,0 @@ -/* - * Copyright (C) 2010 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 android.webkit; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.Canvas; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.SystemClock; -import android.util.FloatMath; -import android.util.Log; -import android.view.ScaleGestureDetector; -import android.view.View; - -/** - * The ZoomManager is responsible for maintaining the WebView's current zoom - * level state. It is also responsible for managing the on-screen zoom controls - * as well as any animation of the WebView due to zooming. - * - * Currently, there are two methods for animating the zoom of a WebView. - * - * (1) The first method is triggered by startZoomAnimation(...) and is a fixed - * length animation where the final zoom scale is known at startup. This type of - * animation notifies webkit of the final scale BEFORE it animates. The animation - * is then done by scaling the CANVAS incrementally based on a stepping function. - * - * (2) The second method is triggered by a multi-touch pinch and the new scale - * is determined dynamically based on the user's gesture. This type of animation - * only notifies webkit of new scale AFTER the gesture is complete. The animation - * effect is achieved by scaling the VIEWS (both WebView and ViewManager.ChildView) - * to the new scale in response to events related to the user's gesture. - */ -class ZoomManager { - - static final String LOGTAG = "webviewZoom"; - - private final WebViewClassic mWebView; - private final CallbackProxy mCallbackProxy; - - // Widgets responsible for the on-screen zoom functions of the WebView. - private ZoomControlEmbedded mEmbeddedZoomControl; - private ZoomControlExternal mExternalZoomControl; - - /* - * The scale factors that determine the upper and lower bounds for the - * default zoom scale. - */ - protected static final float DEFAULT_MAX_ZOOM_SCALE_FACTOR = 4.00f; - protected static final float DEFAULT_MIN_ZOOM_SCALE_FACTOR = 0.25f; - - // The default scale limits, which are dependent on the display density. - private float mDefaultMaxZoomScale; - private float mDefaultMinZoomScale; - - // The actual scale limits, which can be set through a webpage's viewport - // meta-tag. - private float mMaxZoomScale; - private float mMinZoomScale; - - // Locks the minimum ZoomScale to the value currently set in mMinZoomScale. - private boolean mMinZoomScaleFixed = true; - - /* - * When loading a new page the WebView does not initially know the final - * width of the page. Therefore, when a new page is loaded in overview mode - * the overview scale is initialized to a default value. This flag is then - * set and used to notify the ZoomManager to take the width of the next - * picture from webkit and use that width to enter into zoom overview mode. - */ - private boolean mInitialZoomOverview = false; - - /* - * When in the zoom overview mode, the page's width is fully fit to the - * current window. Additionally while the page is in this state it is - * active, in other words, you can click to follow the links. We cache a - * boolean to enable us to quickly check whether or not we are in overview - * mode, but this value should only be modified by changes to the zoom - * scale. - */ - private boolean mInZoomOverview = false; - private int mZoomOverviewWidth; - private float mInvZoomOverviewWidth; - - /* - * These variables track the center point of the zoom and they are used to - * determine the point around which we should zoom. They are stored in view - * coordinates. - */ - private float mZoomCenterX; - private float mZoomCenterY; - - /* - * Similar to mZoomCenterX(Y), these track the focus point of the scale - * gesture. The difference is these get updated every time when onScale is - * invoked no matter if a zooming really happens. - */ - private float mFocusX; - private float mFocusY; - - /* - * mFocusMovementQueue keeps track of the previous focus point movement - * has been through. Comparing to the difference of the gesture's previous - * span and current span, it determines if the gesture is for panning or - * zooming or both. - */ - private FocusMovementQueue mFocusMovementQueue; - - /* - * These values represent the point around which the screen should be - * centered after zooming. In other words it is used to determine the center - * point of the visible document after the page has finished zooming. This - * is important because the zoom may have potentially reflowed the text and - * we need to ensure the proper portion of the document remains on the - * screen. - */ - private int mAnchorX; - private int mAnchorY; - - // The scale factor that is used to determine the column width for text - private float mTextWrapScale; - - /* - * The default zoom scale is the scale factor used when the user triggers a - * zoom in by double tapping on the WebView. The value is initially set - * based on the display density, but can be changed at any time via the - * WebSettings. - */ - private float mDefaultScale; - private float mInvDefaultScale; - - /* - * The logical density of the display. This is a scaling factor for the - * Density Independent Pixel unit, where one DIP is one pixel on an - * approximately 160 dpi screen (see android.util.DisplayMetrics.density) - */ - private float mDisplayDensity; - - /* - * The factor that is used to tweak the zoom scale on a double-tap, - * and can be changed via WebSettings. Range is from 0.75f to 1.25f. - */ - private float mDoubleTapZoomFactor = 1.0f; - - /* - * The scale factor that is used as the minimum increment when going from - * overview to reading level on a double tap. - */ - private static float MIN_DOUBLE_TAP_SCALE_INCREMENT = 0.5f; - - // the current computed zoom scale and its inverse. - private float mActualScale; - private float mInvActualScale; - - /* - * The initial scale for the WebView. 0 means default. If initial scale is - * greater than 0, the WebView starts with this value as its initial scale. - */ - private float mInitialScale; - - private static float MINIMUM_SCALE_INCREMENT = 0.007f; - - /* - * The touch points could be changed even the fingers stop moving. - * We use the following to filter out the zooming jitters. - */ - private static float MINIMUM_SCALE_WITHOUT_JITTER = 0.007f; - - /* - * The following member variables are only to be used for animating zoom. If - * mZoomScale is non-zero then we are in the middle of a zoom animation. The - * other variables are used as a cache (e.g. inverse) or as a way to store - * the state of the view prior to animating (e.g. initial scroll coords). - */ - private float mZoomScale; - private float mInvInitialZoomScale; - private float mInvFinalZoomScale; - private int mInitialScrollX; - private int mInitialScrollY; - private long mZoomStart; - - private static final int ZOOM_ANIMATION_LENGTH = 175; - - // whether support multi-touch - private boolean mSupportMultiTouch; - - /** - * True if we have a touch panel capable of detecting smooth pan/scale at the same time - */ - private boolean mAllowPanAndScale; - - // use the framework's ScaleGestureDetector to handle scaling gestures - private ScaleGestureDetector mScaleDetector; - private boolean mPinchToZoomAnimating = false; - - private boolean mHardwareAccelerated = false; - private boolean mInHWAcceleratedZoom = false; - - public ZoomManager(WebViewClassic webView, CallbackProxy callbackProxy) { - mWebView = webView; - mCallbackProxy = callbackProxy; - - /* - * Ideally mZoomOverviewWidth should be mContentWidth. But sites like - * ESPN and Engadget always have wider mContentWidth no matter what the - * viewport size is. - */ - setZoomOverviewWidth(WebViewClassic.DEFAULT_VIEWPORT_WIDTH); - - mFocusMovementQueue = new FocusMovementQueue(); - } - - /** - * Initialize both the default and actual zoom scale to the given density. - * - * @param density The logical density of the display. This is a scaling factor - * for the Density Independent Pixel unit, where one DIP is one pixel on an - * approximately 160 dpi screen (see android.util.DisplayMetrics.density). - */ - public void init(float density) { - assert density > 0; - - mDisplayDensity = density; - setDefaultZoomScale(density); - mActualScale = density; - mInvActualScale = 1 / density; - mTextWrapScale = getReadingLevelScale(); - } - - /** - * Update the default zoom scale using the given density. It will also reset - * the current min and max zoom scales to the default boundaries as well as - * ensure that the actual scale falls within those boundaries. - * - * @param density The logical density of the display. This is a scaling factor - * for the Density Independent Pixel unit, where one DIP is one pixel on an - * approximately 160 dpi screen (see android.util.DisplayMetrics.density). - */ - public void updateDefaultZoomDensity(float density) { - assert density > 0; - - if (Math.abs(density - mDefaultScale) > MINIMUM_SCALE_INCREMENT) { - // Remember the current zoom density before it gets changed. - final float originalDefault = mDefaultScale; - // set the new default density - mDisplayDensity = density; - setDefaultZoomScale(density); - float scaleChange = (originalDefault > 0.0) ? density / originalDefault: 1.0f; - // adjust the scale if it falls outside the new zoom bounds - setZoomScale(mActualScale * scaleChange, true); - } - } - - private void setDefaultZoomScale(float defaultScale) { - final float originalDefault = mDefaultScale; - mDefaultScale = defaultScale; - mInvDefaultScale = 1 / defaultScale; - mDefaultMaxZoomScale = defaultScale * DEFAULT_MAX_ZOOM_SCALE_FACTOR; - mDefaultMinZoomScale = defaultScale * DEFAULT_MIN_ZOOM_SCALE_FACTOR; - if (originalDefault > 0.0 && mMaxZoomScale > 0.0) { - // Keeps max zoom scale when zoom density changes. - mMaxZoomScale = defaultScale / originalDefault * mMaxZoomScale; - } else { - mMaxZoomScale = mDefaultMaxZoomScale; - } - if (originalDefault > 0.0 && mMinZoomScale > 0.0) { - // Keeps min zoom scale when zoom density changes. - mMinZoomScale = defaultScale / originalDefault * mMinZoomScale; - } else { - mMinZoomScale = mDefaultMinZoomScale; - } - if (!exceedsMinScaleIncrement(mMinZoomScale, mMaxZoomScale)) { - mMaxZoomScale = mMinZoomScale; - } - sanitizeMinMaxScales(); - } - - public final float getScale() { - return mActualScale; - } - - public final float getInvScale() { - return mInvActualScale; - } - - public final float getTextWrapScale() { - return mTextWrapScale; - } - - public final float getMaxZoomScale() { - return mMaxZoomScale; - } - - public final float getMinZoomScale() { - return mMinZoomScale; - } - - public final float getDefaultScale() { - return mDefaultScale; - } - - /** - * Returns the zoom scale used for reading text on a double-tap. - */ - public final float getReadingLevelScale() { - return computeScaleWithLimits(computeReadingLevelScale(getZoomOverviewScale())); - } - - /* package */ final float computeReadingLevelScale(float scale) { - return Math.max(mDisplayDensity * mDoubleTapZoomFactor, - scale + MIN_DOUBLE_TAP_SCALE_INCREMENT); - } - - public final float getInvDefaultScale() { - return mInvDefaultScale; - } - - public final float getDefaultMaxZoomScale() { - return mDefaultMaxZoomScale; - } - - public final float getDefaultMinZoomScale() { - return mDefaultMinZoomScale; - } - - public final int getDocumentAnchorX() { - return mAnchorX; - } - - public final int getDocumentAnchorY() { - return mAnchorY; - } - - public final void clearDocumentAnchor() { - mAnchorX = mAnchorY = 0; - } - - public final void setZoomCenter(float x, float y) { - mZoomCenterX = x; - mZoomCenterY = y; - } - - public final void setInitialScaleInPercent(int scaleInPercent) { - mInitialScale = scaleInPercent * 0.01f; - } - - public final float computeScaleWithLimits(float scale) { - if (scale < mMinZoomScale) { - scale = mMinZoomScale; - } else if (scale > mMaxZoomScale) { - scale = mMaxZoomScale; - } - return scale; - } - - public final boolean isScaleOverLimits(float scale) { - return scale <= mMinZoomScale || scale >= mMaxZoomScale; - } - - public final boolean isZoomScaleFixed() { - return mMinZoomScale >= mMaxZoomScale; - } - - public static final boolean exceedsMinScaleIncrement(float scaleA, float scaleB) { - return Math.abs(scaleA - scaleB) >= MINIMUM_SCALE_INCREMENT; - } - - public boolean willScaleTriggerZoom(float scale) { - return exceedsMinScaleIncrement(scale, mActualScale); - } - - public final boolean canZoomIn() { - return mMaxZoomScale - mActualScale > MINIMUM_SCALE_INCREMENT; - } - - public final boolean canZoomOut() { - return mActualScale - mMinZoomScale > MINIMUM_SCALE_INCREMENT; - } - - public boolean zoomIn() { - return zoom(1.25f); - } - - public boolean zoomOut() { - return zoom(0.8f); - } - - // returns TRUE if zoom out succeeds and FALSE if no zoom changes. - private boolean zoom(float zoomMultiplier) { - mInitialZoomOverview = false; - // TODO: alternatively we can disallow this during draw history mode - mWebView.switchOutDrawHistory(); - // Center zooming to the center of the screen. - mZoomCenterX = mWebView.getViewWidth() * .5f; - mZoomCenterY = mWebView.getViewHeight() * .5f; - mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX()); - mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY()); - return startZoomAnimation(mActualScale * zoomMultiplier, - !mWebView.getSettings().getUseFixedViewport()); - } - - /** - * Initiates an animated zoom of the WebView. - * - * @return true if the new scale triggered an animation and false otherwise. - */ - public boolean startZoomAnimation(float scale, boolean reflowText) { - mInitialZoomOverview = false; - float oldScale = mActualScale; - mInitialScrollX = mWebView.getScrollX(); - mInitialScrollY = mWebView.getScrollY(); - - // snap to reading level scale if it is close - if (!exceedsMinScaleIncrement(scale, getReadingLevelScale())) { - scale = getReadingLevelScale(); - } - - setZoomScale(scale, reflowText); - - if (oldScale != mActualScale) { - if (mHardwareAccelerated) { - mInHWAcceleratedZoom = true; - } - // use mZoomPickerScale to see zoom preview first - mZoomStart = SystemClock.uptimeMillis(); - mInvInitialZoomScale = 1.0f / oldScale; - mInvFinalZoomScale = 1.0f / mActualScale; - mZoomScale = mActualScale; - mWebView.onFixedLengthZoomAnimationStart(); - mWebView.invalidate(); - return true; - } else { - return false; - } - } - - /** - * This method is called by the WebView's drawing code when a fixed length zoom - * animation is occurring. Its purpose is to animate the zooming of the canvas - * to the desired scale which was specified in startZoomAnimation(...). - * - * A fixed length animation begins when startZoomAnimation(...) is called and - * continues until the ZOOM_ANIMATION_LENGTH time has elapsed. During that - * interval each time the WebView draws it calls this function which is - * responsible for generating the animation. - * - * Additionally, the WebView can check to see if such an animation is currently - * in progress by calling isFixedLengthAnimationInProgress(). - */ - public void animateZoom(Canvas canvas) { - mInitialZoomOverview = false; - if (mZoomScale == 0) { - Log.w(LOGTAG, "A WebView is attempting to perform a fixed length " - + "zoom animation when no zoom is in progress"); - // Now that we've logged about it, go ahead and just recover - mInHWAcceleratedZoom = false; - return; - } - - float zoomScale; - int interval = (int) (SystemClock.uptimeMillis() - mZoomStart); - if (interval < ZOOM_ANIMATION_LENGTH) { - float ratio = (float) interval / ZOOM_ANIMATION_LENGTH; - zoomScale = 1.0f / (mInvInitialZoomScale - + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio); - mWebView.invalidate(); - } else { - zoomScale = mZoomScale; - // set mZoomScale to be 0 as we have finished animating - mZoomScale = 0; - mWebView.onFixedLengthZoomAnimationEnd(); - } - // calculate the intermediate scroll position. Since we need to use - // zoomScale, we can't use the WebView's pinLocX/Y functions directly. - float scale = zoomScale * mInvInitialZoomScale; - int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX) - mZoomCenterX); - tx = -WebViewClassic.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth() - * zoomScale)) + mWebView.getScrollX(); - int titleHeight = mWebView.getTitleHeight(); - int ty = Math.round(scale - * (mInitialScrollY + mZoomCenterY - titleHeight) - - (mZoomCenterY - titleHeight)); - ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebViewClassic.pinLoc(ty - - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight() - * zoomScale)) + titleHeight) + mWebView.getScrollY(); - - if (mHardwareAccelerated) { - mWebView.updateScrollCoordinates(mWebView.getScrollX() - tx, mWebView.getScrollY() - ty); - // By adding webView matrix, we need to offset the canvas a bit - // to make the animation smooth. - canvas.translate(tx, ty); - setZoomScale(zoomScale, false); - - if (mZoomScale == 0) { - // We've reached the end of the zoom animation. - mInHWAcceleratedZoom = false; - - // Ensure that the zoom level is pushed to WebCore. This has not - // yet occurred because we prevent it from happening while - // mInHWAcceleratedZoom is true. - mWebView.sendViewSizeZoom(false); - } - } else { - canvas.translate(tx, ty); - canvas.scale(zoomScale, zoomScale); - } - } - - public boolean isZoomAnimating() { - return isFixedLengthAnimationInProgress() || mPinchToZoomAnimating; - } - - public boolean isFixedLengthAnimationInProgress() { - return mZoomScale != 0 || mInHWAcceleratedZoom; - } - - public void updateDoubleTapZoom(int doubleTapZoom) { - boolean zoomIn = (mTextWrapScale - mActualScale) < .1f; - mDoubleTapZoomFactor = doubleTapZoom / 100.0f; - mTextWrapScale = getReadingLevelScale(); - float newScale = zoomIn ? mTextWrapScale - : Math.min(mTextWrapScale, mActualScale); - setZoomScale(newScale, true, true); - } - - public void refreshZoomScale(boolean reflowText) { - setZoomScale(mActualScale, reflowText, true); - } - - public void setZoomScale(float scale, boolean reflowText) { - setZoomScale(scale, reflowText, false); - } - - private void setZoomScale(float scale, boolean reflowText, boolean force) { - final boolean isScaleLessThanMinZoom = scale < mMinZoomScale; - scale = computeScaleWithLimits(scale); - - // determine whether or not we are in the zoom overview mode - if (isScaleLessThanMinZoom && mMinZoomScale < mDefaultScale) { - mInZoomOverview = true; - } else { - mInZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale()); - } - - if (reflowText && !mWebView.getSettings().getUseFixedViewport()) { - mTextWrapScale = scale; - } - - if (scale != mActualScale || force) { - float oldScale = mActualScale; - float oldInvScale = mInvActualScale; - - if (scale != mActualScale && !mPinchToZoomAnimating) { - mCallbackProxy.onScaleChanged(mActualScale, scale); - } - - mActualScale = scale; - mInvActualScale = 1 / scale; - - if (!mWebView.drawHistory() && !mInHWAcceleratedZoom) { - - // If history Picture is drawn, don't update scroll. They will - // be updated when we get out of that mode. - // update our scroll so we don't appear to jump - // i.e. keep the center of the doc in the center of the view - // If this is part of a zoom on a HW accelerated canvas, we - // have already updated the scroll so don't do it again. - int oldX = mWebView.getScrollX(); - int oldY = mWebView.getScrollY(); - float ratio = scale * oldInvScale; - float sx = ratio * oldX + (ratio - 1) * mZoomCenterX; - float sy = ratio * oldY + (ratio - 1) - * (mZoomCenterY - mWebView.getTitleHeight()); - - // Scale all the child views - mWebView.mViewManager.scaleAll(); - - // as we don't have animation for scaling, don't do animation - // for scrolling, as it causes weird intermediate state - int scrollX = mWebView.pinLocX(Math.round(sx)); - int scrollY = mWebView.pinLocY(Math.round(sy)); - if(!mWebView.updateScrollCoordinates(scrollX, scrollY)) { - // the scroll position is adjusted at the beginning of the - // zoom animation. But we want to update the WebKit at the - // end of the zoom animation. See comments in onScaleEnd(). - mWebView.sendOurVisibleRect(); - } - } - - // if the we need to reflow the text then force the VIEW_SIZE_CHANGED - // event to be sent to WebKit - mWebView.sendViewSizeZoom(reflowText); - } - } - - public boolean isDoubleTapEnabled() { - WebSettings settings = mWebView.getSettings(); - return settings != null && settings.getUseWideViewPort(); - } - - /** - * The double tap gesture can result in different behaviors depending on the - * content that is tapped. - * - * (1) PLUGINS: If the taps occur on a plugin then we maximize the plugin on - * the screen. If the plugin is already maximized then zoom the user into - * overview mode. - * - * (2) HTML/OTHER: If the taps occur outside a plugin then the following - * heuristic is used. - * A. If the current text wrap scale differs from newly calculated and the - * layout algorithm specifies the use of NARROW_COLUMNS, then fit to - * column by reflowing the text. - * B. If the page is not in overview mode then change to overview mode. - * C. If the page is in overmode then change to the default scale. - */ - public void handleDoubleTap(float lastTouchX, float lastTouchY) { - // User takes action, set initial zoom overview to false. - mInitialZoomOverview = false; - WebSettingsClassic settings = mWebView.getSettings(); - if (!isDoubleTapEnabled()) { - return; - } - - setZoomCenter(lastTouchX, lastTouchY); - mAnchorX = mWebView.viewToContentX((int) lastTouchX + mWebView.getScrollX()); - mAnchorY = mWebView.viewToContentY((int) lastTouchY + mWebView.getScrollY()); - settings.setDoubleTapToastCount(0); - - // remove the zoom control after double tap - dismissZoomPicker(); - - final float newTextWrapScale; - if (settings.getUseFixedViewport()) { - newTextWrapScale = Math.max(mActualScale, getReadingLevelScale()); - } else { - newTextWrapScale = mActualScale; - } - final boolean firstTimeReflow = !exceedsMinScaleIncrement(mActualScale, mTextWrapScale); - if (firstTimeReflow || mInZoomOverview) { - // In case first time reflow or in zoom overview mode, let reflow and zoom - // happen at the same time. - mTextWrapScale = newTextWrapScale; - } - if (settings.isNarrowColumnLayout() - && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale) - && !firstTimeReflow - && !mInZoomOverview) { - // Reflow only. - mTextWrapScale = newTextWrapScale; - refreshZoomScale(true); - } else if (!mInZoomOverview && willScaleTriggerZoom(getZoomOverviewScale())) { - // Reflow, if necessary. - if (mTextWrapScale > getReadingLevelScale()) { - mTextWrapScale = getReadingLevelScale(); - refreshZoomScale(true); - } - zoomToOverview(); - } else { - zoomToReadingLevel(); - } - } - - private void setZoomOverviewWidth(int width) { - if (width == 0) { - mZoomOverviewWidth = WebViewClassic.DEFAULT_VIEWPORT_WIDTH; - } else { - mZoomOverviewWidth = width; - } - mInvZoomOverviewWidth = 1.0f / width; - } - - /* package */ float getZoomOverviewScale() { - return mWebView.getViewWidth() * mInvZoomOverviewWidth; - } - - public boolean isInZoomOverview() { - return mInZoomOverview; - } - - private void zoomToOverview() { - // Force the titlebar fully reveal in overview mode - int scrollY = mWebView.getScrollY(); - if (scrollY < mWebView.getTitleHeight()) { - mWebView.updateScrollCoordinates(mWebView.getScrollX(), 0); - } - startZoomAnimation(getZoomOverviewScale(), - !mWebView.getSettings().getUseFixedViewport()); - } - - private void zoomToReadingLevel() { - final float readingScale = getReadingLevelScale(); - - int left = mWebView.getBlockLeftEdge(mAnchorX, mAnchorY, readingScale); - if (left != WebViewClassic.NO_LEFTEDGE) { - // add a 5pt padding to the left edge. - int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5)) - - mWebView.getScrollX(); - // Re-calculate the zoom center so that the new scroll x will be - // on the left edge. - if (viewLeft > 0) { - mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale); - } else { - mWebView.getWebView().scrollBy(viewLeft, 0); - mZoomCenterX = 0; - } - } - startZoomAnimation(readingScale, - !mWebView.getSettings().getUseFixedViewport()); - } - - public void updateMultiTouchSupport(Context context) { - // check the preconditions - assert mWebView.getSettings() != null; - - final WebSettings settings = mWebView.getSettings(); - final PackageManager pm = context.getPackageManager(); - mSupportMultiTouch = - (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH) - || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT)) - && settings.supportZoom() && settings.getBuiltInZoomControls(); - mAllowPanAndScale = - pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT) - || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT); - - if (mSupportMultiTouch && (mScaleDetector == null)) { - mScaleDetector = new ScaleGestureDetector(context, new ScaleDetectorListener()); - } else if (!mSupportMultiTouch && (mScaleDetector != null)) { - mScaleDetector = null; - } - } - - public boolean supportsMultiTouchZoom() { - return mSupportMultiTouch; - } - - public boolean supportsPanDuringZoom() { - return mAllowPanAndScale; - } - - /** - * Notifies the caller that the ZoomManager is requesting that scale related - * updates should not be sent to webkit. This can occur in cases where the - * ZoomManager is performing an animation and does not want webkit to update - * until the animation is complete. - * - * @return true if scale related updates should not be sent to webkit and - * false otherwise. - */ - public boolean isPreventingWebkitUpdates() { - // currently only animating a multi-touch zoom and fixed length - // animations prevent updates, but others can add their own conditions - // to this method if necessary. - return isZoomAnimating(); - } - - public ScaleGestureDetector getScaleGestureDetector() { - return mScaleDetector; - } - - private class FocusMovementQueue { - private static final int QUEUE_CAPACITY = 5; - private float[] mQueue; - private float mSum; - private int mSize; - private int mIndex; - - FocusMovementQueue() { - mQueue = new float[QUEUE_CAPACITY]; - mSize = 0; - mSum = 0; - mIndex = 0; - } - - private void clear() { - mSize = 0; - mSum = 0; - mIndex = 0; - for (int i = 0; i < QUEUE_CAPACITY; ++i) { - mQueue[i] = 0; - } - } - - private void add(float focusDelta) { - mSum += focusDelta; - if (mSize < QUEUE_CAPACITY) { // fill up the queue. - mSize++; - } else { // circulate the queue. - mSum -= mQueue[mIndex]; - } - mQueue[mIndex] = focusDelta; - mIndex = (mIndex + 1) % QUEUE_CAPACITY; - } - - private float getSum() { - return mSum; - } - } - - private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener { - private float mAccumulatedSpan; - - public boolean onScaleBegin(ScaleGestureDetector detector) { - mInitialZoomOverview = false; - dismissZoomPicker(); - mFocusMovementQueue.clear(); - mFocusX = detector.getFocusX(); - mFocusY = detector.getFocusY(); - mWebView.mViewManager.startZoom(); - mWebView.onPinchToZoomAnimationStart(); - mAccumulatedSpan = 0; - return true; - } - - // If the user moves the fingers but keeps the same distance between them, - // we should do panning only. - public boolean isPanningOnly(ScaleGestureDetector detector) { - float prevFocusX = mFocusX; - float prevFocusY = mFocusY; - mFocusX = detector.getFocusX(); - mFocusY = detector.getFocusY(); - float focusDelta = (prevFocusX == 0 && prevFocusY == 0) ? 0 : - FloatMath.sqrt((mFocusX - prevFocusX) * (mFocusX - prevFocusX) - + (mFocusY - prevFocusY) * (mFocusY - prevFocusY)); - mFocusMovementQueue.add(focusDelta); - float deltaSpan = detector.getCurrentSpan() - detector.getPreviousSpan() + - mAccumulatedSpan; - final boolean result = mFocusMovementQueue.getSum() > Math.abs(deltaSpan); - if (result) { - mAccumulatedSpan += deltaSpan; - } else { - mAccumulatedSpan = 0; - } - return result; - } - - public boolean handleScale(ScaleGestureDetector detector) { - float scale = detector.getScaleFactor() * mActualScale; - - // if scale is limited by any reason, don't zoom but do ask - // the detector to update the event. - boolean isScaleLimited = - isScaleOverLimits(scale) || scale < getZoomOverviewScale(); - - // Prevent scaling beyond overview scale. - scale = Math.max(computeScaleWithLimits(scale), getZoomOverviewScale()); - - if (mPinchToZoomAnimating || willScaleTriggerZoom(scale)) { - mPinchToZoomAnimating = true; - // limit the scale change per step - if (scale > mActualScale) { - scale = Math.min(scale, mActualScale * 1.25f); - } else { - scale = Math.max(scale, mActualScale * 0.8f); - } - scale = computeScaleWithLimits(scale); - // if the scale change is too small, regard it as jitter and skip it. - if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_WITHOUT_JITTER) { - return isScaleLimited; - } - setZoomCenter(detector.getFocusX(), detector.getFocusY()); - setZoomScale(scale, false); - mWebView.invalidate(); - return true; - } - return isScaleLimited; - } - - public boolean onScale(ScaleGestureDetector detector) { - if (isPanningOnly(detector) || handleScale(detector)) { - mFocusMovementQueue.clear(); - return true; - } - return false; - } - - public void onScaleEnd(ScaleGestureDetector detector) { - if (mPinchToZoomAnimating) { - mPinchToZoomAnimating = false; - mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX()); - mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY()); - // don't reflow when zoom in; when zoom out, do reflow if the - // new scale is almost minimum scale. - boolean reflowNow = !canZoomOut() || (mActualScale <= 0.8 * mTextWrapScale); - // force zoom after mPreviewZoomOnly is set to false so that the - // new view size will be passed to the WebKit - refreshZoomScale(reflowNow && - !mWebView.getSettings().getUseFixedViewport()); - // call invalidate() to draw without zoom filter - mWebView.invalidate(); - } - - mWebView.mViewManager.endZoom(); - mWebView.onPinchToZoomAnimationEnd(detector); - } - } - - private void sanitizeMinMaxScales() { - if (mMinZoomScale > mMaxZoomScale) { - Log.w(LOGTAG, "mMinZoom > mMaxZoom!!! " + mMinZoomScale + " > " + mMaxZoomScale, - new Exception()); - mMaxZoomScale = mMinZoomScale; - } - } - - public void onSizeChanged(int w, int h, int ow, int oh) { - // reset zoom and anchor to the top left corner of the screen - // unless we are already zooming - if (!isFixedLengthAnimationInProgress()) { - int visibleTitleHeight = mWebView.getVisibleTitleHeight(); - mZoomCenterX = 0; - mZoomCenterY = visibleTitleHeight; - mAnchorX = mWebView.viewToContentX(mWebView.getScrollX()); - mAnchorY = mWebView.viewToContentY(visibleTitleHeight + mWebView.getScrollY()); - } - - // update mMinZoomScale if the minimum zoom scale is not fixed - if (!mMinZoomScaleFixed) { - // when change from narrow screen to wide screen, the new viewWidth - // can be wider than the old content width. We limit the minimum - // scale to 1.0f. The proper minimum scale will be calculated when - // the new picture shows up. - mMinZoomScale = Math.min(1.0f, (float) mWebView.getViewWidth() - / (mWebView.drawHistory() ? mWebView.getHistoryPictureWidth() - : mZoomOverviewWidth)); - // limit the minZoomScale to the initialScale if it is set - if (mInitialScale > 0 && mInitialScale < mMinZoomScale) { - mMinZoomScale = mInitialScale; - } - sanitizeMinMaxScales(); - } - - dismissZoomPicker(); - - // onSizeChanged() is called during WebView layout. And any - // requestLayout() is blocked during layout. As refreshZoomScale() will - // cause its child View to reposition itself through ViewManager's - // scaleAll(), we need to post a Runnable to ensure requestLayout(). - // Additionally, only update the text wrap scale if the width changed. - mWebView.getWebView().post(new PostScale(w != ow && - !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview, w < ow)); - } - - private class PostScale implements Runnable { - final boolean mUpdateTextWrap; - // Remember the zoom overview state right after rotation since - // it could be changed between the time this callback is initiated and - // the time it's actually run. - final boolean mInZoomOverviewBeforeSizeChange; - final boolean mInPortraitMode; - - public PostScale(boolean updateTextWrap, - boolean inZoomOverview, - boolean inPortraitMode) { - mUpdateTextWrap = updateTextWrap; - mInZoomOverviewBeforeSizeChange = inZoomOverview; - mInPortraitMode = inPortraitMode; - } - - public void run() { - if (mWebView.getWebViewCore() != null) { - // we always force, in case our height changed, in which case we - // still want to send the notification over to webkit. - // Keep overview mode unchanged when rotating. - float newScale = mActualScale; - if (mWebView.getSettings().getUseWideViewPort() && - mInPortraitMode && - mInZoomOverviewBeforeSizeChange) { - newScale = getZoomOverviewScale(); - } - setZoomScale(newScale, mUpdateTextWrap, true); - // update the zoom buttons as the scale can be changed - updateZoomPicker(); - } - } - } - - public void updateZoomRange(WebViewCore.ViewState viewState, - int viewWidth, int minPrefWidth) { - if (viewState.mMinScale == 0) { - if (viewState.mMobileSite) { - if (minPrefWidth > Math.max(0, viewWidth)) { - mMinZoomScale = (float) viewWidth / minPrefWidth; - mMinZoomScaleFixed = false; - } else { - mMinZoomScale = viewState.mDefaultScale; - mMinZoomScaleFixed = true; - } - } else { - mMinZoomScale = mDefaultMinZoomScale; - mMinZoomScaleFixed = false; - } - } else { - mMinZoomScale = viewState.mMinScale; - mMinZoomScaleFixed = true; - } - if (viewState.mMaxScale == 0) { - mMaxZoomScale = mDefaultMaxZoomScale; - } else { - mMaxZoomScale = viewState.mMaxScale; - } - sanitizeMinMaxScales(); - } - - /** - * Updates zoom values when Webkit produces a new picture. This method - * should only be called from the UI thread's message handler. - * - * @return True if zoom value has changed - */ - public boolean onNewPicture(WebViewCore.DrawData drawData) { - final int viewWidth = mWebView.getViewWidth(); - final boolean zoomOverviewWidthChanged = setupZoomOverviewWidth(drawData, viewWidth); - final float newZoomOverviewScale = getZoomOverviewScale(); - WebSettingsClassic settings = mWebView.getSettings(); - if (zoomOverviewWidthChanged && settings.isNarrowColumnLayout() && - settings.getUseFixedViewport() && - (mInitialZoomOverview || mInZoomOverview)) { - // Keep mobile site's text wrap scale unchanged. For mobile sites, - // the text wrap scale is the same as zoom overview scale. - if (exceedsMinScaleIncrement(mTextWrapScale, mDefaultScale) || - exceedsMinScaleIncrement(newZoomOverviewScale, mDefaultScale)) { - mTextWrapScale = getReadingLevelScale(); - } else { - mTextWrapScale = newZoomOverviewScale; - } - } - - if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) { - mMinZoomScale = newZoomOverviewScale; - mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale); - sanitizeMinMaxScales(); - } - // fit the content width to the current view for the first new picture - // after first layout. - boolean scaleHasDiff = exceedsMinScaleIncrement(newZoomOverviewScale, mActualScale); - // Make sure the actual scale is no less than zoom overview scale. - boolean scaleLessThanOverview = - (newZoomOverviewScale - mActualScale) >= MINIMUM_SCALE_INCREMENT; - // Make sure mobile sites are correctly handled since mobile site will - // change content width after rotating. - boolean mobileSiteInOverview = mInZoomOverview && - !exceedsMinScaleIncrement(newZoomOverviewScale, mDefaultScale); - if (!mWebView.drawHistory() && - ((scaleLessThanOverview && settings.getUseWideViewPort())|| - ((mInitialZoomOverview || mobileSiteInOverview) && - scaleHasDiff && zoomOverviewWidthChanged))) { - mInitialZoomOverview = false; - setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) && - !mWebView.getSettings().getUseFixedViewport()); - } else { - mInZoomOverview = !scaleHasDiff; - } - if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) { - // Set mInitialZoomOverview in case this is the first picture for non standard load, - // so next new picture could be forced into overview mode if it's true. - mInitialZoomOverview = mInZoomOverview; - } - - return scaleHasDiff; - } - - /** - * Set up correct zoom overview width based on different settings. - * - * @param drawData webviewcore draw data - * @param viewWidth current view width - */ - private boolean setupZoomOverviewWidth(WebViewCore.DrawData drawData, final int viewWidth) { - WebSettings settings = mWebView.getSettings(); - int newZoomOverviewWidth = mZoomOverviewWidth; - if (settings.getUseWideViewPort()) { - if (drawData.mContentSize.x > 0) { - // The webkitDraw for layers will not populate contentSize, and it'll be - // ignored for zoom overview width update. - newZoomOverviewWidth = Math.min(WebViewClassic.sMaxViewportWidth, - drawData.mContentSize.x); - } - } else { - // If not use wide viewport, use view width as the zoom overview width. - newZoomOverviewWidth = Math.round(viewWidth / mDefaultScale); - } - if (newZoomOverviewWidth != mZoomOverviewWidth) { - setZoomOverviewWidth(newZoomOverviewWidth); - return true; - } - return false; - } - - /** - * Updates zoom values after Webkit completes the initial page layout. It - * is called when visiting a page for the first time as well as when the - * user navigates back to a page (in which case we may need to restore the - * zoom levels to the state they were when you left the page). This method - * should only be called from the UI thread's message handler. - */ - public void onFirstLayout(WebViewCore.DrawData drawData) { - // precondition check - assert drawData != null; - assert drawData.mViewState != null; - assert mWebView.getSettings() != null; - - WebViewCore.ViewState viewState = drawData.mViewState; - final Point viewSize = drawData.mViewSize; - updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth); - setupZoomOverviewWidth(drawData, mWebView.getViewWidth()); - final float overviewScale = getZoomOverviewScale(); - WebSettingsClassic settings = mWebView.getSettings(); - if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) { - mMinZoomScale = (mInitialScale > 0) ? - Math.min(mInitialScale, overviewScale) : overviewScale; - mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale); - sanitizeMinMaxScales(); - } - - if (!mWebView.drawHistory()) { - float scale; - if (mInitialScale > 0) { - scale = mInitialScale; - } else if (viewState.mIsRestored || viewState.mViewScale > 0) { - scale = (viewState.mViewScale > 0) - ? viewState.mViewScale : overviewScale; - mTextWrapScale = (viewState.mTextWrapScale > 0) - ? viewState.mTextWrapScale : getReadingLevelScale(); - } else { - scale = overviewScale; - if (!settings.getUseWideViewPort() - || !settings.getLoadWithOverviewMode()) { - scale = Math.max(mDefaultScale, scale); - } - if (settings.isNarrowColumnLayout() && - settings.getUseFixedViewport()) { - // When first layout, reflow using the reading level scale to avoid - // reflow when double tapped. - mTextWrapScale = getReadingLevelScale(); - } - } - boolean reflowText = false; - if (!viewState.mIsRestored) { - if (settings.getUseFixedViewport()) { - // Override the scale only in case of fixed viewport. - scale = Math.max(scale, overviewScale); - mTextWrapScale = Math.max(mTextWrapScale, overviewScale); - } - reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale); - } - mInitialZoomOverview = settings.getLoadWithOverviewMode() && - !exceedsMinScaleIncrement(scale, overviewScale); - setZoomScale(scale, reflowText); - - // update the zoom buttons as the scale can be changed - updateZoomPicker(); - } - } - - public void saveZoomState(Bundle b) { - b.putFloat("scale", mActualScale); - b.putFloat("textwrapScale", mTextWrapScale); - b.putBoolean("overview", mInZoomOverview); - } - - public void restoreZoomState(Bundle b) { - // as getWidth() / getHeight() of the view are not available yet, set up - // mActualScale, so that when onSizeChanged() is called, the rest will - // be set correctly - mActualScale = b.getFloat("scale", 1.0f); - mInvActualScale = 1 / mActualScale; - mTextWrapScale = b.getFloat("textwrapScale", mActualScale); - mInZoomOverview = b.getBoolean("overview"); - } - - private ZoomControlBase getCurrentZoomControl() { - if (mWebView.getSettings() != null && mWebView.getSettings().supportZoom()) { - if (mWebView.getSettings().getBuiltInZoomControls()) { - if ((mEmbeddedZoomControl == null) - && mWebView.getSettings().getDisplayZoomControls()) { - mEmbeddedZoomControl = new ZoomControlEmbedded(this, mWebView); - } - return mEmbeddedZoomControl; - } else { - if (mExternalZoomControl == null) { - mExternalZoomControl = new ZoomControlExternal(mWebView); - } - return mExternalZoomControl; - } - } - return null; - } - - public void invokeZoomPicker() { - ZoomControlBase control = getCurrentZoomControl(); - if (control != null) { - control.show(); - } - } - - public void dismissZoomPicker() { - ZoomControlBase control = getCurrentZoomControl(); - if (control != null) { - control.hide(); - } - } - - public boolean isZoomPickerVisible() { - ZoomControlBase control = getCurrentZoomControl(); - return (control != null) ? control.isVisible() : false; - } - - public void updateZoomPicker() { - ZoomControlBase control = getCurrentZoomControl(); - if (control != null) { - control.update(); - } - } - - /** - * The embedded zoom control intercepts touch events and automatically stays - * visible. The external control needs to constantly refresh its internal - * timer to stay visible. - */ - public void keepZoomPickerVisible() { - ZoomControlBase control = getCurrentZoomControl(); - if (control != null && control == mExternalZoomControl) { - control.show(); - } - } - - public View getExternalZoomPicker() { - ZoomControlBase control = getCurrentZoomControl(); - if (control != null && control == mExternalZoomControl) { - return mExternalZoomControl.getControls(); - } else { - return null; - } - } - - public void setHardwareAccelerated() { - mHardwareAccelerated = true; - } - - /** - * OnPageFinished called by webview when a page is fully loaded. - */ - /* package*/ void onPageFinished(String url) { - // Turn off initial zoom overview flag when a page is fully loaded. - mInitialZoomOverview = false; - } -} |