diff options
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; - } -} |