diff options
author | tedchoc@chromium.org <tedchoc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 18:45:00 +0000 |
---|---|---|
committer | tedchoc@chromium.org <tedchoc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 18:45:00 +0000 |
commit | ca6b8608f99aff601b7899d89aa1b748f855fc83 (patch) | |
tree | da2bef2339fec03787f08536ee703724f62056e0 /content | |
parent | 4529c84eac99324912c3ad0418b20e56d2782c77 (diff) | |
download | chromium_src-ca6b8608f99aff601b7899d89aa1b748f855fc83.zip chromium_src-ca6b8608f99aff601b7899d89aa1b748f855fc83.tar.gz chromium_src-ca6b8608f99aff601b7899d89aa1b748f855fc83.tar.bz2 |
Upstream WebSettings and ZoomManager for ContentShell.
WebSettings has dependencies on ZoomManager, so it is easier to bring them
up together.
BUG=
TEST=
Review URL: https://chromiumcodereview.appspot.com/10573020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143214 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/app/android/content_jni_registrar.cc | 2 | ||||
-rw-r--r-- | content/browser/android/content_view_impl.h | 6 | ||||
-rw-r--r-- | content/browser/android/web_settings.cc | 314 | ||||
-rw-r--r-- | content/browser/android/web_settings.h | 55 | ||||
-rw-r--r-- | content/content_browser.gypi | 2 | ||||
-rw-r--r-- | content/content_jni.gypi | 2 | ||||
-rw-r--r-- | content/public/android/java/org/chromium/content/browser/ContentView.java | 284 | ||||
-rw-r--r-- | content/public/android/java/org/chromium/content/browser/WebSettings.java | 678 | ||||
-rw-r--r-- | content/public/android/java/org/chromium/content/browser/ZoomManager.java | 211 |
9 files changed, 1524 insertions, 30 deletions
diff --git a/content/app/android/content_jni_registrar.cc b/content/app/android/content_jni_registrar.cc index 61f2192..3ac6dc2 100644 --- a/content/app/android/content_jni_registrar.cc +++ b/content/app/android/content_jni_registrar.cc @@ -16,6 +16,7 @@ #include "content/browser/android/download_controller.h" #include "content/browser/android/sandboxed_process_launcher.h" #include "content/browser/android/touch_point.h" +#include "content/browser/android/web_settings.h" #include "content/common/android/command_line.h" #include "content/common/android/surface_callback.h" #include "content/common/android/trace_event_binding.h" @@ -37,6 +38,7 @@ base::android::RegistrationMethod kContentRegisteredMethods[] = { { "TouchPoint", content::RegisterTouchPoint }, { "TraceEvent", RegisterTraceEvent }, { "UserAgent", content::RegisterUserAgent }, + { "WebSettings", WebSettings::RegisterWebSettings }, }; bool RegisterJni(JNIEnv* env) { diff --git a/content/browser/android/content_view_impl.h b/content/browser/android/content_view_impl.h index 5550434..3b890d1 100644 --- a/content/browser/android/content_view_impl.h +++ b/content/browser/android/content_view_impl.h @@ -24,6 +24,12 @@ class ContentViewImpl : public ContentView, WebContents* web_contents); virtual void Destroy(JNIEnv* env, jobject obj); + // -------------------------------------------------------------------------- + // Methods called from native code + // -------------------------------------------------------------------------- + + WebContents* web_contents() const { return web_contents_; } + private: // NotificationObserver implementation. virtual void Observe(int type, diff --git a/content/browser/android/web_settings.cc b/content/browser/android/web_settings.cc new file mode 100644 index 0000000..71a09a46 --- /dev/null +++ b/content/browser/android/web_settings.cc @@ -0,0 +1,314 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/android/web_settings.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "content/browser/android/content_view_impl.h" +#include "content/browser/renderer_host/render_view_host_delegate.h" +#include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/public/browser/web_contents.h" +#include "webkit/glue/user_agent.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webpreferences.h" + +// TODO(tedchoc): Remove once the JNI generator has been updated to allow +// namespace specification. +namespace content { +#include "jni/web_settings_jni.h" +} + +using base::android::CheckException; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertUTF16ToJavaString; +using base::android::ConvertUTF8ToJavaString; +using base::android::GetClass; +using base::android::GetFieldID; +using base::android::GetMethodIDFromClassName; +using base::android::ScopedJavaLocalRef; +using webkit_glue::WebPreferences; + +namespace content { + +struct WebSettings::FieldIds { + // Note on speed. One may think that an approach that reads field values via + // JNI is ineffective and should not be used. Please keep in mind that in the + // legacy WebView the whole Sync method took <1ms on Xoom, and no one is + // expected to modify settings in performance-critical code. + FieldIds() { } + + FieldIds(JNIEnv* env) { + const char* kStringClassName = "Ljava/lang/String;"; + + // FIXME: we should be using a new GetFieldIDFromClassName() with caching. + ScopedJavaLocalRef<jclass> clazz( + GetClass(env, "org/chromium/content/browser/WebSettings")); + standard_fond_family = + GetFieldID(env, clazz, "mStandardFontFamily", kStringClassName); + fixed_font_family = + GetFieldID(env, clazz, "mFixedFontFamily", kStringClassName); + sans_serif_font_family = + GetFieldID(env, clazz, "mSansSerifFontFamily", kStringClassName); + serif_font_family = + GetFieldID(env, clazz, "mSerifFontFamily", kStringClassName); + cursive_font_family = + GetFieldID(env, clazz, "mCursiveFontFamily", kStringClassName); + fantasy_font_family = + GetFieldID(env, clazz, "mFantasyFontFamily", kStringClassName); + default_text_encoding = + GetFieldID(env, clazz, "mDefaultTextEncoding", kStringClassName); + user_agent = + GetFieldID(env, clazz, "mUserAgent", kStringClassName); + minimum_font_size = GetFieldID(env, clazz, "mMinimumFontSize", "I"); + minimum_logical_font_size = + GetFieldID(env, clazz, "mMinimumLogicalFontSize", "I"); + default_font_size = GetFieldID(env, clazz, "mDefaultFontSize", "I"); + default_fixed_font_size = + GetFieldID(env, clazz, "mDefaultFixedFontSize", "I"); + load_images_automatically = + GetFieldID(env, clazz, "mLoadsImagesAutomatically", "Z"); + java_script_enabled = + GetFieldID(env, clazz, "mJavaScriptEnabled", "Z"); + java_script_can_open_windows_automatically = + GetFieldID(env, clazz, "mJavaScriptCanOpenWindowsAutomatically", "Z"); + } + + // Field ids + jfieldID standard_fond_family; + jfieldID fixed_font_family; + jfieldID sans_serif_font_family; + jfieldID serif_font_family; + jfieldID cursive_font_family; + jfieldID fantasy_font_family; + jfieldID default_text_encoding; + jfieldID user_agent; + jfieldID minimum_font_size; + jfieldID minimum_logical_font_size; + jfieldID default_font_size; + jfieldID default_fixed_font_size; + jfieldID load_images_automatically; + jfieldID java_script_enabled; + jfieldID java_script_can_open_windows_automatically; +}; + +WebSettings::WebSettings(JNIEnv* env, + jobject obj, + content::WebContents* contents, + bool is_master_mode) + : content::WebContentsObserver(contents), + is_master_mode_(is_master_mode) { + web_settings_.Reset(env, obj); +} + +WebSettings::~WebSettings() { +} + +// static +bool WebSettings::RegisterWebSettings(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void WebSettings::Destroy(JNIEnv* env, jobject obj) { + delete this; +} + +void WebSettings::SyncFromNativeImpl() { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + if (!field_ids_.get()) { + field_ids_.reset(new FieldIds(env)); + } + + jobject obj = web_settings_.obj(); + content::RenderViewHost* render_view_host = + web_contents()->GetRenderViewHost(); + WebPreferences prefs = render_view_host->GetDelegate()->GetWebkitPrefs(); + + ScopedJavaLocalRef<jstring> str = + ConvertUTF16ToJavaString(env, + prefs.standard_font_family_map[WebPreferences::kCommonScript]); + env->SetObjectField(obj, field_ids_->standard_fond_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF16ToJavaString(env, + prefs.fixed_font_family_map[WebPreferences::kCommonScript])); + env->SetObjectField(obj, field_ids_->fixed_font_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF16ToJavaString(env, + prefs.sans_serif_font_family_map[WebPreferences::kCommonScript])); + env->SetObjectField(obj, field_ids_->sans_serif_font_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF16ToJavaString(env, + prefs.serif_font_family_map[WebPreferences::kCommonScript])); + env->SetObjectField(obj, field_ids_->serif_font_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF16ToJavaString(env, + prefs.cursive_font_family_map[WebPreferences::kCommonScript])); + env->SetObjectField(obj, field_ids_->cursive_font_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF16ToJavaString(env, + prefs.fantasy_font_family_map[WebPreferences::kCommonScript])); + env->SetObjectField(obj, field_ids_->fantasy_font_family, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF8ToJavaString(env, prefs.default_encoding)); + env->SetObjectField(obj, field_ids_->default_text_encoding, str.obj()); + CheckException(env); + + str.Reset(ConvertUTF8ToJavaString(env, webkit_glue::GetUserAgent(GURL("")))); + env->SetObjectField(obj, field_ids_->user_agent, str.obj()); + CheckException(env); + + env->SetIntField(obj, field_ids_->minimum_font_size, + prefs.minimum_font_size); + CheckException(env); + + env->SetIntField( + obj, + field_ids_->minimum_logical_font_size, prefs.minimum_logical_font_size); + CheckException(env); + + env->SetIntField(obj, field_ids_->default_font_size, + prefs.default_font_size); + CheckException(env); + + env->SetIntField( + obj, field_ids_->default_fixed_font_size, prefs.default_fixed_font_size); + CheckException(env); + + env->SetBooleanField( + obj, + field_ids_->load_images_automatically, prefs.loads_images_automatically); + CheckException(env); + + env->SetBooleanField( + obj, field_ids_->java_script_enabled, prefs.javascript_enabled); + CheckException(env); + + env->SetBooleanField( + obj, + field_ids_->java_script_can_open_windows_automatically, + prefs.javascript_can_open_windows_automatically); + CheckException(env); + + Java_WebSettings_setPluginsDisabled(env, obj, !prefs.plugins_enabled); + CheckException(env); +} + +void WebSettings::SyncToNativeImpl() { + JNIEnv* env = base::android::AttachCurrentThread(); + CHECK(env); + if (!field_ids_.get()) { + field_ids_.reset(new FieldIds(env)); + } + + jobject obj = web_settings_.obj(); + content::RenderViewHost* render_view_host = + web_contents()->GetRenderViewHost(); + WebPreferences prefs = render_view_host->GetDelegate()->GetWebkitPrefs(); + + ScopedJavaLocalRef<jstring> str( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->standard_fond_family))); + prefs.standard_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->fixed_font_family))); + prefs.fixed_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->sans_serif_font_family))); + prefs.sans_serif_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->serif_font_family))); + prefs.serif_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->cursive_font_family))); + prefs.cursive_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->fantasy_font_family))); + prefs.fantasy_font_family_map[WebPreferences::kCommonScript] = + ConvertJavaStringToUTF16(str); + + str.Reset( + env, static_cast<jstring>( + env->GetObjectField(obj, field_ids_->default_text_encoding))); + prefs.default_encoding = ConvertJavaStringToUTF8(str); + + prefs.minimum_font_size = + env->GetIntField(obj, field_ids_->minimum_font_size); + + prefs.minimum_logical_font_size = + env->GetIntField(obj, field_ids_->minimum_logical_font_size); + + prefs.default_font_size = + env->GetIntField(obj, field_ids_->default_font_size); + + prefs.default_fixed_font_size = + env->GetIntField(obj, field_ids_->default_fixed_font_size); + + prefs.loads_images_automatically = + env->GetBooleanField(obj, field_ids_->load_images_automatically); + + prefs.javascript_enabled = + env->GetBooleanField(obj, field_ids_->java_script_enabled); + + prefs.javascript_can_open_windows_automatically = env->GetBooleanField( + obj, field_ids_->java_script_can_open_windows_automatically); + + prefs.plugins_enabled = !Java_WebSettings_getPluginsDisabled(env, obj); + + render_view_host->UpdateWebkitPreferences(prefs); +} + +void WebSettings::SyncFromNative(JNIEnv* env, jobject obj) { + SyncFromNativeImpl(); +} + +void WebSettings::SyncToNative(JNIEnv* env, jobject obj) { + SyncToNativeImpl(); +} + +void WebSettings::RenderViewCreated(content::RenderViewHost*) { + if (is_master_mode_) { + SyncToNativeImpl(); + } +} + +static jint Init( + JNIEnv* env, jobject obj, jint nativeContentView, + jboolean is_master_mode) { + content::WebContents* web_contents = + reinterpret_cast<content::ContentViewImpl*>(nativeContentView) + ->web_contents(); + WebSettings* web_settings = + new WebSettings(env, obj, web_contents, is_master_mode); + return reinterpret_cast<jint>(web_settings); +} + +static jstring GetDefaultUserAgent(JNIEnv* env, jclass clazz) { + // "Version/4.0" had been hardcoded in the legacy WebView. + std::string ua = webkit_glue::BuildUserAgentFromProduct("Version/4.0"); + return base::android::ConvertUTF8ToJavaString(env, ua).Release(); +} + +} // namespace content diff --git a/content/browser/android/web_settings.h b/content/browser/android/web_settings.h new file mode 100644 index 0000000..de8a3d7 --- /dev/null +++ b/content/browser/android/web_settings.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_WEB_SETTINGS_H_ +#define CONTENT_BROWSER_ANDROID_WEB_SETTINGS_H_ +#pragma once + +#include <jni.h> + +#include "base/android/scoped_java_ref.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/web_contents_observer.h" + +namespace content { + +class WebSettings : public WebContentsObserver { + public: + WebSettings(JNIEnv* env, jobject obj, + WebContents* contents, + bool is_master_mode); + virtual ~WebSettings(); + + static bool RegisterWebSettings(JNIEnv* env); + + void Destroy(JNIEnv* env, jobject obj); + + // Synchronizes the Java settings from native settings. + void SyncFromNative(JNIEnv* env, jobject obj); + // Synchronizes the native settings from Java settings. + void SyncToNative(JNIEnv* env, jobject obj); + + private: + struct FieldIds; + + void SyncFromNativeImpl(); + void SyncToNativeImpl(); + + // WebContentsObserver overrides: + virtual void RenderViewCreated(RenderViewHost*) OVERRIDE; + + // Determines whether a sync to native should be triggered when a new render + // view is created. + bool is_master_mode_; + + // Java field references for accessing the values in the Java object. + scoped_ptr<FieldIds> field_ids_; + + // The Java counterpart to this class. + base::android::ScopedJavaGlobalRef<jobject> web_settings_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_WEB_SETTINGS_H_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index d7adcf3..e5489bd 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -205,6 +205,8 @@ 'browser/android/sandboxed_process_launcher.h', 'browser/android/touch_point.cc', 'browser/android/touch_point.h', + 'browser/android/web_settings.cc', + 'browser/android/web_settings.h', 'browser/appcache/appcache_dispatcher_host.cc', 'browser/appcache/appcache_dispatcher_host.h', 'browser/appcache/appcache_frontend_proxy.cc', diff --git a/content/content_jni.gypi b/content/content_jni.gypi index 21acb5a..9b0054c 100644 --- a/content/content_jni.gypi +++ b/content/content_jni.gypi @@ -22,6 +22,7 @@ 'public/android/java/org/chromium/content/browser/LocationProvider.java', 'public/android/java/org/chromium/content/browser/SandboxedProcessLauncher.java', 'public/android/java/org/chromium/content/browser/TouchPoint.java', + 'public/android/java/org/chromium/content/browser/WebSettings.java', 'public/android/java/org/chromium/content/common/CommandLine.java', 'public/android/java/org/chromium/content/common/SurfaceCallback.java', 'public/android/java/org/chromium/content/common/TraceEvent.java', @@ -40,6 +41,7 @@ '<(SHARED_INTERMEDIATE_DIR)/content/jni/location_provider_jni.h', '<(SHARED_INTERMEDIATE_DIR)/content/jni/sandboxed_process_launcher_jni.h', '<(SHARED_INTERMEDIATE_DIR)/content/jni/touch_point_jni.h', + '<(SHARED_INTERMEDIATE_DIR)/content/jni/web_settings_jni.h', '<(SHARED_INTERMEDIATE_DIR)/content/jni/command_line_jni.h', '<(SHARED_INTERMEDIATE_DIR)/content/jni/surface_callback_jni.h', '<(SHARED_INTERMEDIATE_DIR)/content/jni/trace_event_jni.h', diff --git a/content/public/android/java/org/chromium/content/browser/ContentView.java b/content/public/android/java/org/chromium/content/browser/ContentView.java index 98e6a3a..aa2fc11 100644 --- a/content/public/android/java/org/chromium/content/browser/ContentView.java +++ b/content/public/android/java/org/chromium/content/browser/ContentView.java @@ -7,11 +7,12 @@ package org.chromium.content.browser; import android.content.Context; import android.util.AttributeSet; import android.util.Log; +import android.view.View; import android.webkit.DownloadListener; import android.widget.FrameLayout; import org.chromium.base.WeakContext; -import org.chromium.content.browser.AndroidBrowserProcess; +import org.chromium.content.common.TraceEvent; public class ContentView extends FrameLayout { private static final String TAG = "ContentView"; @@ -33,15 +34,33 @@ public class ContentView extends FrameLayout { public static final int MAX_RENDERERS_SINGLE_PROCESS = AndroidBrowserProcess.MAX_RENDERERS_SINGLE_PROCESS; + // Used to avoid enabling zooming in / out in WebView zoom controls + // if resulting zooming will produce little visible difference. + private static float WEBVIEW_ZOOM_CONTROLS_EPSILON = 0.007f; + // content_view_client.cc depends on ContentView.java holding a ref to the current client // instance since the native side only holds a weak pointer to the client. We chose this // solution over the managed object owning the C++ object's memory since it's a lot simpler // in terms of clean up. private ContentViewClient mContentViewClient; + private WebSettings mWebSettings; + // Native pointer to C++ ContentView object which will be set by nativeInit() private int mNativeContentView = 0; + private ZoomManager mZoomManager; + + // Cached page scale factor from native + private float mNativePageScaleFactor = 1.0f; + private float mNativeMinimumScale = 1.0f; + private float mNativeMaximumScale = 1.0f; + + // TODO(klobag): this is to avoid a bug in GestureDetector. With multi-touch, + // mAlwaysInTapRegion is not reset. So when the last finger is up, onSingleTapUp() + // will be mistakenly fired. + private boolean mIgnoreSingleTap; + // The legacy webview DownloadListener. private DownloadListener mDownloadListener; // ContentViewDownloadDelegate adds support for authenticated downloads @@ -49,33 +68,6 @@ public class ContentView extends FrameLayout { // over DownloadListener. private ContentViewDownloadDelegate mDownloadDelegate; - public void setContentViewClient(ContentViewClient client) { - if (client == null) { - throw new IllegalArgumentException("The client can't be null."); - } - mContentViewClient = client; - if (mNativeContentView != 0) { - - // TODO(jrg): upstream this chain. nativeSetClient(), - // ContentView::SetClient(), add a content_view_client_, - // add web_contents_, pass web_contents into native ContentView ctor, ... - /* nativeSetClient(mNativeContentView, mContentViewClient); */ - } - } - - ContentViewClient getContentViewClient() { - if (mContentViewClient == null) { - // We use the Null Object pattern to avoid having to perform a null check in this class. - // We create it lazily because most of the time a client will be set almost immediately - // after ChromeView is created. - mContentViewClient = new ContentViewClient(); - // We don't set the native ContentViewClient pointer here on purpose. The native - // implementation doesn't mind a null delegate and using one is better than passing a - // Null Object, since we cut down on the number of JNI calls. - } - return mContentViewClient; - } - /** * Enable multi-process ContentView. This should be called by the application before * constructing any ContentView instances. If enabled, ContentView will run renderers in @@ -133,10 +125,32 @@ public class ContentView extends FrameLayout { // TODO(jrg): incomplete; upstream the rest of this method. private void initialize(Context context, int nativeWebContents, int personality) { mNativeContentView = nativeInit(nativeWebContents); + + mPersonality = personality; + mWebSettings = new WebSettings(this, mNativeContentView); + + initGestureDetectors(context); + Log.i(TAG, "mNativeContentView=0x"+ Integer.toHexString(mNativeContentView)); } /** + * @return Whether the configured personality of this ContentView is {@link #PERSONALITY_VIEW}. + */ + boolean isPersonalityView() { + switch (mPersonality) { + case PERSONALITY_VIEW: + return true; + case PERSONALITY_CHROME: + return false; + default: + Log.e(TAG, "Unknown ContentView personality: " + mPersonality); + return false; + } + } + + + /** * Destroy the internal state of the WebView. This method may only be called * after the WebView has been removed from the view system. No other methods * may be called on this WebView after this method has been called. @@ -147,18 +161,62 @@ public class ContentView extends FrameLayout { nativeDestroy(mNativeContentView); mNativeContentView = 0; } + if (mWebSettings != null) { + mWebSettings.destroy(); + mWebSettings = null; + } + } + + /** + * Returns true initially, false after destroy() has been called. + * It is illegal to call any other public method after destroy(). + */ + public boolean isAlive() { + return mNativeContentView != 0; + } + + public void setContentViewClient(ContentViewClient client) { + if (client == null) { + throw new IllegalArgumentException("The client can't be null."); + } + mContentViewClient = client; + if (mNativeContentView != 0) { + + // TODO(jrg): upstream this chain. nativeSetClient(), + // ContentView::SetClient(), add a content_view_client_, + // add web_contents_, pass web_contents into native ContentView ctor, ... + /* nativeSetClient(mNativeContentView, mContentViewClient); */ + } } + ContentViewClient getContentViewClient() { + if (mContentViewClient == null) { + // We use the Null Object pattern to avoid having to perform a null check in this class. + // We create it lazily because most of the time a client will be set almost immediately + // after ContentView is created. + mContentViewClient = new ContentViewClient(); + // We don't set the native ContentViewClient pointer here on purpose. The native + // implementation doesn't mind a null delegate and using one is better than passing a + // Null Object, since we cut down on the number of JNI calls. + } + return mContentViewClient; + } /** - * Load url without fixing up the url string. Calls from Chrome should be not - * be using this, but should use Tab.loadUrl instead. + * Load url without fixing up the url string. Consumers of ContentView are responsible for + * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left + * off during user input). + * * @param url The url to load. */ public void loadUrlWithoutUrlSanitization(String url) { // TODO(tedchoc): Implement. } + void setAllUserAgentOverridesInHistory() { + // TODO(tedchoc): Pass user agent override down to native. + } + /** * Get the URL of the current page. * @@ -207,6 +265,79 @@ public class ContentView extends FrameLayout { } /** + * Start pinch zoom. You must call {@link #pinchEnd} to stop. + */ + void pinchBegin(long timeMs, int x, int y) { + if (mNativeContentView != 0) { + // TODO(tedchoc): Pass pinch begin to native. + } + } + + /** + * Stop pinch zoom. + */ + void pinchEnd(long timeMs) { + if (mNativeContentView != 0) { + // TODO(tedchoc): Pass pinch end to native. + } + } + + void setIgnoreSingleTap(boolean value) { + mIgnoreSingleTap = value; + } + + /** + * Modify the ContentView magnification level. The effect of calling this + * method is exactly as after "pinch zoom". + * + * @param timeMs The event time in milliseconds. + * @param delta The ratio of the new magnification level over the current + * magnification level. + * @param anchorX The magnification anchor (X) in the current view + * coordinate. + * @param anchorY The magnification anchor (Y) in the current view + * coordinate. + */ + void pinchBy(long timeMs, int anchorX, int anchorY, float delta) { + if (mNativeContentView != 0) { + // TODO(tedchoc): Pass pinch by to native. + } + } + + /** + * Return the WebSettings object used to control the settings for this + * WebView. + * + * Note that when ContentView is used in the PERSONALITY_CHROME role, + * WebSettings can only be used for retrieving settings values. For + * modifications, ChromeNativePreferences is to be used. + * @return A WebSettings object that can be used to control this WebView's + * settings. + */ + public WebSettings getSettings() { + return mWebSettings; + } + + private void initGestureDetectors(final Context context) { + try { + TraceEvent.begin(); + // TODO(tedchoc): Upstream the rest of the initialization. + mZoomManager = new ZoomManager(context, this); + mZoomManager.updateMultiTouchSupport(); + } finally { + TraceEvent.end(); + } + } + + void updateMultiTouchZoomSupport() { + mZoomManager.updateMultiTouchSupport(); + } + + public boolean isMultiTouchZoomSupported() { + return mZoomManager.isMultiTouchZoomSupported(); + } + + /** * Register the listener to be used when content can not be handled by the * rendering engine, and should be downloaded instead. This will replace the * current listener. @@ -242,6 +373,99 @@ public class ContentView extends FrameLayout { } /** + * Checks whether the WebView can be zoomed in. + * + * @return True if the WebView can be zoomed in. + */ + // This method uses the term 'zoom' for legacy reasons, but relates + // to what chrome calls the 'page scale factor'. + public boolean canZoomIn() { + return mNativeMaximumScale - mNativePageScaleFactor > WEBVIEW_ZOOM_CONTROLS_EPSILON; + } + + /** + * Checks whether the WebView can be zoomed out. + * + * @return True if the WebView can be zoomed out. + */ + // This method uses the term 'zoom' for legacy reasons, but relates + // to what chrome calls the 'page scale factor'. + public boolean canZoomOut() { + return mNativePageScaleFactor - mNativeMinimumScale > WEBVIEW_ZOOM_CONTROLS_EPSILON; + } + + /** + * Zooms in the WebView by 25% (or less if that would result in zooming in + * more than possible). + * + * @return True if there was a zoom change, false otherwise. + */ + // This method uses the term 'zoom' for legacy reasons, but relates + // to what chrome calls the 'page scale factor'. + public boolean zoomIn() { + if (!canZoomIn()) { + return false; + } + + if (mNativeContentView == 0) { + return false; + } + + long timeMs = System.currentTimeMillis(); + int x = getWidth() / 2; + int y = getHeight() / 2; + float delta = 1.25f; + + pinchBegin(timeMs, x, y); + pinchBy(timeMs, x, y, delta); + pinchEnd(timeMs); + + return true; + } + + /** + * Zooms out the WebView by 20% (or less if that would result in zooming out + * more than possible). + * + * @return True if there was a zoom change, false otherwise. + */ + // This method uses the term 'zoom' for legacy reasons, but relates + // to what chrome calls the 'page scale factor'. + public boolean zoomOut() { + if (!canZoomOut()) { + return false; + } + + if (mNativeContentView == 0) { + return false; + } + + long timeMs = System.currentTimeMillis(); + int x = getWidth() / 2; + int y = getHeight() / 2; + float delta = 0.8f; + + pinchBegin(timeMs, x, y); + pinchBy(timeMs, x, y, delta); + pinchEnd(timeMs); + + return true; + } + + // Invokes the graphical zoom picker widget for this ContentView. + public void invokeZoomPicker() { + if (mWebSettings.supportZoom()) { + mZoomManager.invokeZoomPicker(); + } + } + + // Unlike legacy WebView getZoomControls which returns external zoom controls, + // this method returns built-in zoom controls. This method is used in tests. + public View getZoomControlsForTest() { + return mZoomManager.getZoomControlsViewForTest(); + } + + /** * Initialize the ContentView native side. * Should be called with a valid native WebContents. * If nativeInitProcess is never called, the first time this method is called, nativeInitProcess diff --git a/content/public/android/java/org/chromium/content/browser/WebSettings.java b/content/public/android/java/org/chromium/content/browser/WebSettings.java new file mode 100644 index 0000000..7650a15 --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/WebSettings.java @@ -0,0 +1,678 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.content.browser; + +import android.os.Handler; +import android.os.Message; +import android.webkit.WebSettings.PluginState; +import android.webkit.WebView; + +import org.chromium.base.CalledByNative; +import org.chromium.base.ThreadUtils; + +/** + * Manages settings state for a ContentView. A WebSettings instance is obtained + * from ContentView.getSettings(). If ContentView is used in the + * ContentView.PERSONALITY_VIEW role, all settings are read / write. If ContentView + * is in the ContentView.PERSONALITY_CHROME role, setting can only be read. + */ +public class WebSettings { + private static final String TAG = "WebSettings"; + + // This class must be created on the UI thread. Afterwards, it can be + // used from any thread. Internally, the class uses a message queue + // to call native code on the UI thread only. + + private int mNativeWebSettings = 0; + + private ContentView mContentView; + + // When ContentView is used in PERSONALITY_CHROME mode, settings can't + // be modified through the WebSettings instance. + private boolean mCanModifySettings; + + // A flag to avoid sending superfluous synchronization messages. + private boolean mIsSyncMessagePending = false; + // Custom handler that queues messages to call native code on the UI thread. + private final EventHandler mEventHandler; + + private static final int MINIMUM_FONT_SIZE = 1; + private static final int MAXIMUM_FONT_SIZE = 72; + + // Private settings so we don't have to go into native code to + // retrieve the values. After setXXX, sendSyncMessage() needs to be called. + // + // TODO(mnaganov): populate with the complete set of legacy WebView settings. + + 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"; + // FIXME: Should be obtained from Android. Problem: it is hidden. + private String mDefaultTextEncoding = "Latin-1"; + private String mUserAgent; + private int mMinimumFontSize = 8; + private int mMinimumLogicalFontSize = 8; + private int mDefaultFontSize = 16; + private int mDefaultFixedFontSize = 13; + private boolean mLoadsImagesAutomatically = true; + private boolean mJavaScriptEnabled = false; + private boolean mJavaScriptCanOpenWindowsAutomatically = false; + private PluginState mPluginState = PluginState.OFF; + + // Not accessed by the native side. + private String mDefaultUserAgent = ""; + private boolean mSupportZoom = true; + private boolean mBuiltInZoomControls = false; + private boolean mDisplayZoomControls = true; + + // Class to handle messages to be processed on the UI thread. + private class EventHandler { + // Message id for syncing + private static final int SYNC = 0; + // Message id for updating user agent in the view + private static final int UPDATE_UA = 1; + // Message id for updating multi-touch zoom state in the view + private static final int UPDATE_MULTI_TOUCH = 2; + // Actual UI thread handler + private Handler mHandler; + + EventHandler() { + mHandler = mContentView.isPersonalityView() ? + new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SYNC: + synchronized (WebSettings.this) { + nativeSyncToNative(mNativeWebSettings); + mIsSyncMessagePending = false; + } + break; + case UPDATE_UA: + synchronized (mContentView) { + mContentView.setAllUserAgentOverridesInHistory(); + } + break; + case UPDATE_MULTI_TOUCH: + synchronized (mContentView) { + mContentView.updateMultiTouchZoomSupport(); + } + break; + } + } + } : + new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SYNC: + synchronized (WebSettings.this) { + nativeSyncFromNative(mNativeWebSettings); + mIsSyncMessagePending = false; + } + break; + } + } + }; + } + + private synchronized void sendSyncMessage() { + mHandler.sendMessage(Message.obtain(null, SYNC)); + } + + private synchronized void sendUpdateUaMessage() { + mHandler.sendMessage(Message.obtain(null, UPDATE_UA)); + } + + private synchronized void sendUpdateMultiTouchMessage() { + mHandler.sendMessage(Message.obtain(null, UPDATE_MULTI_TOUCH)); + } + } + + /** + * Package constructor to prevent clients from creating a new settings + * instance. Must be called on the UI thread. + */ + WebSettings(ContentView contentView, int nativeContentView) { + ThreadUtils.assertOnUiThread(); + mContentView = contentView; + mCanModifySettings = mContentView.isPersonalityView(); + mNativeWebSettings = nativeInit(nativeContentView, mCanModifySettings); + assert mNativeWebSettings != 0; + + mEventHandler = new EventHandler(); + if (mCanModifySettings) { + // PERSONALITY_VIEW + mDefaultUserAgent = nativeGetDefaultUserAgent(); + mUserAgent = mDefaultUserAgent; + nativeSyncToNative(mNativeWebSettings); + } else { + // PERSONALITY_CHROME + // Chrome has zooming enabled by default. These settings are not + // set by the native code. + mBuiltInZoomControls = true; + mDisplayZoomControls = false; + nativeSyncFromNative(mNativeWebSettings); + } + } + + /** + * Destroys the native side of the WebSettings. This WebSettings object + * cannot be used after this method has been called. Should only be called + * when related ContentView is destroyed. + */ + void destroy() { + nativeDestroy(mNativeWebSettings); + mNativeWebSettings = 0; + } + + /** + * Set the WebView's user-agent string. If the string "ua" is null or empty, + * it will use the system default user-agent string. + */ + public synchronized void setUserAgentString(String ua) { + assert mCanModifySettings; + final String oldUserAgent = mUserAgent; + if (ua == null || ua.length() == 0) { + mUserAgent = mDefaultUserAgent; + } else { + mUserAgent = ua; + } + if (!oldUserAgent.equals(mUserAgent)) { + mEventHandler.sendUpdateUaMessage(); + } + } + + /** + * Gets the WebView's user-agent string. + */ + public synchronized String getUserAgentString() { + // TODO(mnaganov): Doesn't reflect changes made by ChromeNativePreferences. + return mUserAgent; + } + + /** + * Sets whether the WebView should support zooming using its on-screen zoom + * controls and gestures. The particular zoom mechanisms that should be used + * can be set with {@link #setBuiltInZoomControls}. This setting does not + * affect zooming performed using the {@link WebView#zoomIn()} and + * {@link WebView#zoomOut()} methods. The default is true. + * + * @param support whether the WebView should support zoom + */ + public void setSupportZoom(boolean support) { + mSupportZoom = support; + mEventHandler.sendUpdateMultiTouchMessage(); + } + + /** + * Gets whether the WebView supports zoom. + * + * @return true if the WebView supports zoom + * @see #setSupportZoom + */ + public boolean supportZoom() { + return mSupportZoom; + } + + /** + * Sets whether the WebView should use its built-in zoom mechanisms. The + * built-in zoom mechanisms comprise on-screen zoom controls, which are + * displayed over the WebView's content, and the use of a pinch gesture to + * control zooming. Whether or not these on-screen controls are displayed + * can be set with {@link #setDisplayZoomControls}. The default is false, + * due to compatibility reasons. + * <p> + * The built-in mechanisms are the only currently supported zoom + * mechanisms, so it is recommended that this setting is always enabled. + * In other words, there is no point of calling this method other than + * with the 'true' parameter. + * + * @param enabled whether the WebView should use its built-in zoom mechanisms + */ + public void setBuiltInZoomControls(boolean enabled) { + mBuiltInZoomControls = enabled; + mEventHandler.sendUpdateMultiTouchMessage(); + } + + /** + * Gets whether the zoom mechanisms built into WebView are being used. + * + * @return true if the zoom mechanisms built into WebView are being used + * @see #setBuiltInZoomControls + */ + public boolean getBuiltInZoomControls() { + return mBuiltInZoomControls; + } + + /** + * Sets whether the WebView should display on-screen zoom controls when + * using the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. + * The default is true. + * + * @param enabled whether the WebView should display on-screen zoom controls + */ + public void setDisplayZoomControls(boolean enabled) { + mDisplayZoomControls = enabled; + mEventHandler.sendUpdateMultiTouchMessage(); + } + + /** + * Gets whether the WebView displays on-screen zoom controls when using + * the built-in zoom mechanisms. + * + * @return true if the WebView displays on-screen zoom controls when using + * the built-in zoom mechanisms + * @see #setDisplayZoomControls + */ + public boolean getDisplayZoomControls() { + return mDisplayZoomControls; + } + + boolean supportsMultiTouchZoom() { + return mSupportZoom && mBuiltInZoomControls; + } + + boolean shouldDisplayZoomControls() { + return supportsMultiTouchZoom() && mDisplayZoomControls; + } + + /** + * Set the standard font family name. + * @param font A font family name. + */ + public synchronized void setStandardFontFamily(String font) { + assert mCanModifySettings; + if (!mStandardFontFamily.equals(font)) { + mStandardFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the standard font family name. The default is "sans-serif". + * @return The standard font family name as a string. + */ + public synchronized String getStandardFontFamily() { + return mStandardFontFamily; + } + + /** + * Set the fixed font family name. + * @param font A font family name. + */ + public synchronized void setFixedFontFamily(String font) { + assert mCanModifySettings; + if (!mFixedFontFamily.equals(font)) { + mFixedFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the fixed font family name. The default is "monospace". + * @return The fixed font family name as a string. + */ + public synchronized String getFixedFontFamily() { + return mFixedFontFamily; + } + + /** + * Set the sans-serif font family name. + * @param font A font family name. + */ + public synchronized void setSansSerifFontFamily(String font) { + assert mCanModifySettings; + if (!mSansSerifFontFamily.equals(font)) { + mSansSerifFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the sans-serif font family name. + * @return The sans-serif font family name as a string. + */ + public synchronized String getSansSerifFontFamily() { + return mSansSerifFontFamily; + } + + /** + * Set the serif font family name. The default is "sans-serif". + * @param font A font family name. + */ + public synchronized void setSerifFontFamily(String font) { + assert mCanModifySettings; + if (!mSerifFontFamily.equals(font)) { + mSerifFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the serif font family name. The default is "serif". + * @return The serif font family name as a string. + */ + public synchronized String getSerifFontFamily() { + return mSerifFontFamily; + } + + /** + * Set the cursive font family name. + * @param font A font family name. + */ + public synchronized void setCursiveFontFamily(String font) { + assert mCanModifySettings; + if (!mCursiveFontFamily.equals(font)) { + mCursiveFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the cursive font family name. The default is "cursive". + * @return The cursive font family name as a string. + */ + public synchronized String getCursiveFontFamily() { + return mCursiveFontFamily; + } + + /** + * Set the fantasy font family name. + * @param font A font family name. + */ + public synchronized void setFantasyFontFamily(String font) { + assert mCanModifySettings; + if (!mFantasyFontFamily.equals(font)) { + mFantasyFontFamily = font; + sendSyncMessage(); + } + } + + /** + * Get the fantasy font family name. The default is "fantasy". + * @return The fantasy font family name as a string. + */ + public synchronized String getFantasyFontFamily() { + return mFantasyFontFamily; + } + + /** + * Set the minimum font size. + * @param size A non-negative integer between 1 and 72. + * Any number outside the specified range will be pinned. + */ + public synchronized void setMinimumFontSize(int size) { + assert mCanModifySettings; + size = clipFontSize(size); + if (mMinimumFontSize != size) { + mMinimumFontSize = size; + sendSyncMessage(); + } + } + + /** + * Get the minimum font size. The default is 8. + * @return A non-negative integer between 1 and 72. + */ + public synchronized int getMinimumFontSize() { + return mMinimumFontSize; + } + + /** + * Set the minimum logical font size. + * @param size A non-negative integer between 1 and 72. + * Any number outside the specified range will be pinned. + */ + public synchronized void setMinimumLogicalFontSize(int size) { + assert mCanModifySettings; + size = clipFontSize(size); + if (mMinimumLogicalFontSize != size) { + mMinimumLogicalFontSize = size; + sendSyncMessage(); + } + } + + /** + * Get the minimum logical font size. The default is 8. + * @return A non-negative integer between 1 and 72. + */ + public synchronized int getMinimumLogicalFontSize() { + return mMinimumLogicalFontSize; + } + + /** + * Set the default font size. + * @param size A non-negative integer between 1 and 72. + * Any number outside the specified range will be pinned. + */ + public synchronized void setDefaultFontSize(int size) { + assert mCanModifySettings; + size = clipFontSize(size); + if (mDefaultFontSize != size) { + mDefaultFontSize = size; + sendSyncMessage(); + } + } + + /** + * Get the default font size. The default is 16. + * @return A non-negative integer between 1 and 72. + */ + public synchronized int getDefaultFontSize() { + return mDefaultFontSize; + } + + /** + * Set the default fixed font size. + * @param size A non-negative integer between 1 and 72. + * Any number outside the specified range will be pinned. + */ + public synchronized void setDefaultFixedFontSize(int size) { + assert mCanModifySettings; + size = clipFontSize(size); + if (mDefaultFixedFontSize != size) { + mDefaultFixedFontSize = size; + sendSyncMessage(); + } + } + + /** + * Get the default fixed font size. The default is 16. + * @return A non-negative integer between 1 and 72. + */ + public synchronized int getDefaultFixedFontSize() { + return mDefaultFixedFontSize; + } + + /** + * Tell the WebView to enable JavaScript execution. + * + * @param flag True if the WebView should execute JavaScript. + */ + public synchronized void setJavaScriptEnabled(boolean flag) { + assert mCanModifySettings; + if (mJavaScriptEnabled != flag) { + mJavaScriptEnabled = flag; + sendSyncMessage(); + } + } + + /** + * Tell the WebView to load image resources automatically. + * @param flag True if the WebView should load images automatically. + */ + public synchronized void setLoadsImagesAutomatically(boolean flag) { + assert mCanModifySettings; + if (mLoadsImagesAutomatically != flag) { + mLoadsImagesAutomatically = flag; + sendSyncMessage(); + } + } + + /** + * Return true if the WebView will load image resources automatically. + * The default is true. + * @return True if the WebView loads images automatically. + */ + public synchronized boolean getLoadsImagesAutomatically() { + return mLoadsImagesAutomatically; + } + + /** + * Return true if JavaScript is enabled. <b>Note: The default is false.</b> + * + * @return True if JavaScript is enabled. + */ + public synchronized boolean getJavaScriptEnabled() { + return mJavaScriptEnabled; + } + + /** + * Tell the WebView to enable plugins. + * @param flag True if the WebView should load plugins. + * @deprecated This method has been deprecated in favor of + * {@link #setPluginState} + */ + @Deprecated + public synchronized void setPluginsEnabled(boolean flag) { + assert mCanModifySettings; + setPluginState(flag ? PluginState.ON : PluginState.OFF); + } + + /** + * Tell the WebView to enable, disable, or have plugins on demand. On + * demand mode means that if a plugin exists that can handle the embedded + * content, a placeholder icon will be shown instead of the plugin. When + * the placeholder is clicked, the plugin will be enabled. + * @param state One of the PluginState values. + */ + public synchronized void setPluginState(PluginState state) { + assert mCanModifySettings; + if (mPluginState != state) { + mPluginState = state; + sendSyncMessage(); + } + } + + /** + * Return true if plugins are enabled. + * @return True if plugins are enabled. + * @deprecated This method has been replaced by {@link #getPluginState} + */ + @Deprecated + public synchronized boolean getPluginsEnabled() { + return mPluginState == PluginState.ON; + } + + /** + * Return true if plugins are disabled. + * @return True if plugins are disabled. + * @hide + */ + @CalledByNative + private synchronized boolean getPluginsDisabled() { + return mPluginState == PluginState.OFF; + } + + /** + * Sets if plugins are disabled. + * @return True if plugins are disabled. + * @hide + */ + @CalledByNative + private synchronized void setPluginsDisabled(boolean disabled) { + mPluginState = disabled ? PluginState.OFF : PluginState.ON; + } + + /** + * Return the current plugin state. + * @return A value corresponding to the enum PluginState. + */ + public synchronized PluginState getPluginState() { + return mPluginState; + } + + + /** + * Tell javascript to open windows automatically. This applies to the + * javascript function window.open(). + * @param flag True if javascript can open windows automatically. + */ + public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean flag) { + assert mCanModifySettings; + if (mJavaScriptCanOpenWindowsAutomatically != flag) { + mJavaScriptCanOpenWindowsAutomatically = flag; + sendSyncMessage(); + } + } + + /** + * Return true if javascript can open windows automatically. The default + * is false. + * @return True if javascript can open windows automatically during + * window.open(). + */ + public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() { + return mJavaScriptCanOpenWindowsAutomatically; + } + + /** + * Set the default text encoding name to use when decoding html pages. + * @param encoding The text encoding name. + */ + public synchronized void setDefaultTextEncodingName(String encoding) { + assert mCanModifySettings; + if (!mDefaultTextEncoding.equals(encoding)) { + mDefaultTextEncoding = encoding; + sendSyncMessage(); + } + } + + /** + * Get the default text encoding name. The default is "Latin-1". + * @return The default text encoding name as a string. + */ + public synchronized String getDefaultTextEncodingName() { + return mDefaultTextEncoding; + } + + private int clipFontSize(int size) { + if (size < MINIMUM_FONT_SIZE) { + return MINIMUM_FONT_SIZE; + } else if (size > MAXIMUM_FONT_SIZE) { + return MAXIMUM_FONT_SIZE; + } + return size; + } + + // Send a synchronization message to handle syncing the native settings. + // When the ContentView is in the PERSONALITY_VIEW role, called from setter + // methods. When the ContentView is in the PERSONALITY_CHROME role, called + // from ContentView on Chrome preferences updates. + synchronized void sendSyncMessage() { + // Only post if a sync is not pending + if (!mIsSyncMessagePending) { + mIsSyncMessagePending = true; + mEventHandler.sendSyncMessage(); + } + } + + // Initialize the WebSettings native side. + private native int nativeInit(int contentViewPtr, boolean isMasterMode); + + private native void nativeDestroy(int nativeWebSettings); + + private static native String nativeGetDefaultUserAgent(); + + // Synchronize Java settings from native settings. + private native void nativeSyncFromNative(int nativeWebSettings); + + // Synchronize native settings from Java settings. + private native void nativeSyncToNative(int nativeWebSettings); +} diff --git a/content/public/android/java/org/chromium/content/browser/ZoomManager.java b/content/public/android/java/org/chromium/content/browser/ZoomManager.java new file mode 100644 index 0000000..8e7ae08 --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/ZoomManager.java @@ -0,0 +1,211 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.content.browser; + +import android.content.Context; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ZoomButtonsController; + +/** + * The ZoomManager is responsible for maintaining the ContentView's current zoom + * level state. It is also responsible for managing the on-screen zoom controls. + */ +class ZoomManager { + private static final String TAG = "ContentViewZoom"; + + private ContentView mContentView; + private ZoomButtonsController mZoomButtonsController; + + private class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener { + // Completely silence scaling events. Used in WebView when zoom support + // is turned off. + private boolean mPermanentlyIgnoreDetectorEvents = false; + // Bypass events through the detector to maintain its state. Used when + // renderes already handles the touch event. + private boolean mTemporarilyIgnoreDetectorEvents = false; + + // Whether any pinch zoom event has been sent to native. + private boolean mPinchEventSent; + + boolean getPermanentlyIgnoreDetectorEvents() { + return mPermanentlyIgnoreDetectorEvents; + } + + void setPermanentlyIgnoreDetectorEvents(boolean value) { + // Note that returning false from onScaleBegin / onScale makes the + // gesture detector not to emit further scaling notifications + // related to this gesture. Thus, if detector events are enabled in + // the middle of the gesture, we don't need to do anything. + mPermanentlyIgnoreDetectorEvents = value; + } + + void setTemporarilyIgnoreDetectorEvents(boolean value) { + mTemporarilyIgnoreDetectorEvents = value; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + if (ignoreDetectorEvents()) return false; + mPinchEventSent = false; + mContentView.setIgnoreSingleTap(true); + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + if (!mPinchEventSent || !mContentView.isAlive()) return; + mContentView.pinchEnd(detector.getEventTime()); + mPinchEventSent = false; + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (ignoreDetectorEvents()) return false; + // It is possible that pinchBegin() was never called when we reach here. + // This happens when webkit handles the 2nd touch down event. That causes + // ContentView to ignore the onScaleBegin() call. And if webkit does not + // handle the touch move events afterwards, we will face a situation + // that pinchBy() is called without any pinchBegin(). + // To solve this problem, we call pinchBegin() here if it is never called. + if (!mPinchEventSent) { + mContentView.pinchBegin(detector.getEventTime(), + (int) detector.getFocusX(), (int) detector.getFocusY()); + mPinchEventSent = true; + } + mContentView.pinchBy( + detector.getEventTime(), (int) detector.getFocusX(), (int) detector.getFocusY(), + detector.getScaleFactor()); + return true; + } + + private boolean ignoreDetectorEvents() { + return mPermanentlyIgnoreDetectorEvents || + mTemporarilyIgnoreDetectorEvents || + !mContentView.isAlive(); + } + } + + private ScaleGestureDetector mMultiTouchDetector; + private ScaleGestureListener mMultiTouchListener; + + ZoomManager(final Context context, ContentView contentView) { + mContentView = contentView; + mMultiTouchListener = new ScaleGestureListener(); + mMultiTouchDetector = new ScaleGestureDetector(context, mMultiTouchListener); + } + + void invokeZoomPicker() { + ZoomButtonsController zoomControls = getZoomControls(); + if (zoomControls != null && !zoomControls.isVisible()) { + zoomControls.setVisible(true); + } + } + + boolean isMultiTouchZoomSupported() { + return !mMultiTouchListener.getPermanentlyIgnoreDetectorEvents(); + } + + boolean isScaleGestureDetectionInProgress() { + return isMultiTouchZoomSupported() + && mMultiTouchDetector.isInProgress(); + } + + // Passes the touch event to ScaleGestureDetector so that its internal + // state won't go wrong, but instructs the listener to ignore the result + // of processing, if any. + void passTouchEventThrough(MotionEvent event) { + mMultiTouchListener.setTemporarilyIgnoreDetectorEvents(true); + try { + mMultiTouchDetector.onTouchEvent(event); + } catch (Exception e) { + Log.e(TAG, "ScaleGestureDetector got into a bad state!", e); + assert(false); + } + } + + // Passes the touch event to ScaleGestureDetector so that its internal state + // won't go wrong. ScaleGestureDetector needs two pointers in a MotionEvent + // to recognize a zoom gesture. + boolean processTouchEvent(MotionEvent event) { + // TODO: Need to deal with multi-touch transition + mMultiTouchListener.setTemporarilyIgnoreDetectorEvents(false); + try { + return mMultiTouchDetector.onTouchEvent(event); + } catch (Exception e) { + Log.e(TAG, "ScaleGestureDetector got into a bad state!", e); + assert(false); + } + return false; + } + + void updateMultiTouchSupport() { + mMultiTouchListener.setPermanentlyIgnoreDetectorEvents( + !mContentView.getSettings().supportsMultiTouchZoom()); + } + + private ZoomButtonsController getZoomControls() { + if (mZoomButtonsController == null && + mContentView.getSettings().shouldDisplayZoomControls()) { + mZoomButtonsController = new ZoomButtonsController(mContentView); + 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.RIGHT; + } + } + return mZoomButtonsController; + } + + // This method is used in tests. It doesn't modify the state of zoom controls. + View getZoomControlsViewForTest() { + return mZoomButtonsController != null ? mZoomButtonsController.getZoomControls() : null; + } + + void updateZoomControls() { + if (mZoomButtonsController == null) return; + boolean canZoomIn = mContentView.canZoomIn(); + boolean canZoomOut = mContentView.canZoomOut(); + 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 class ZoomListener implements ZoomButtonsController.OnZoomListener { + @Override + public void onVisibilityChanged(boolean visible) { + if (visible) { + // Bring back the hidden zoom controls. + mZoomButtonsController.getZoomControls().setVisibility(View.VISIBLE); + updateZoomControls(); + } + } + + @Override + public void onZoom(boolean zoomIn) { + if (zoomIn) { + mContentView.zoomIn(); + } else { + mContentView.zoomOut(); + } + // ContentView will call updateZoomControls after its current page scale + // is got updated from the native code. + } + } +} |