1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "android_webview/native/aw_settings.h" 6 7 #include "android_webview/browser/renderer_host/aw_render_view_host_ext.h" 8 #include "android_webview/common/aw_content_client.h" 9 #include "android_webview/native/aw_contents.h" 10 #include "base/android/jni_android.h" 11 #include "base/android/jni_string.h" 12 #include "base/supports_user_data.h" 13 #include "content/public/browser/navigation_controller.h" 14 #include "content/public/browser/navigation_entry.h" 15 #include "content/public/browser/render_view_host.h" 16 #include "content/public/browser/web_contents.h" 17 #include "content/public/common/renderer_preferences.h" 18 #include "jni/AwSettings_jni.h" 19 #include "ui/gfx/font_render_params_linux.h" 20 #include "webkit/common/webpreferences.h" 21 22 using base::android::ConvertJavaStringToUTF16; 23 using base::android::ConvertUTF8ToJavaString; 24 using base::android::ScopedJavaLocalRef; 25 using content::RendererPreferences; 26 27 namespace android_webview { 28 29 namespace { 30 31 // TODO(boliu): Deduplicate with chrome/ code. 32 content::RendererPreferencesHintingEnum GetRendererPreferencesHintingEnum( 33 gfx::FontRenderParams::Hinting hinting) { 34 switch (hinting) { 35 case gfx::FontRenderParams::HINTING_NONE: 36 return content::RENDERER_PREFERENCES_HINTING_NONE; 37 case gfx::FontRenderParams::HINTING_SLIGHT: 38 return content::RENDERER_PREFERENCES_HINTING_SLIGHT; 39 case gfx::FontRenderParams::HINTING_MEDIUM: 40 return content::RENDERER_PREFERENCES_HINTING_MEDIUM; 41 case gfx::FontRenderParams::HINTING_FULL: 42 return content::RENDERER_PREFERENCES_HINTING_FULL; 43 default: 44 NOTREACHED() << "Unhandled hinting style " << hinting; 45 return content::RENDERER_PREFERENCES_HINTING_SYSTEM_DEFAULT; 46 } 47 } 48 49 // TODO(boliu): Deduplicate with chrome/ code. 50 content::RendererPreferencesSubpixelRenderingEnum 51 GetRendererPreferencesSubpixelRenderingEnum( 52 gfx::FontRenderParams::SubpixelRendering subpixel_rendering) { 53 switch (subpixel_rendering) { 54 case gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE: 55 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_NONE; 56 case gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB: 57 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_RGB; 58 case gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR: 59 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_BGR; 60 case gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB: 61 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VRGB; 62 case gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR: 63 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VBGR; 64 default: 65 NOTREACHED() << "Unhandled subpixel rendering style " 66 << subpixel_rendering; 67 return content::RENDERER_PREFERENCES_SUBPIXEL_RENDERING_SYSTEM_DEFAULT; 68 } 69 } 70 71 void PopulateFixedRendererPreferences(RendererPreferences* prefs) { 72 prefs->tap_multiple_targets_strategy = 73 content::TAP_MULTIPLE_TARGETS_STRATEGY_NONE; 74 75 // TODO(boliu): Deduplicate with chrome/ code. 76 const gfx::FontRenderParams& params = gfx::GetDefaultWebKitFontRenderParams(); 77 prefs->should_antialias_text = params.antialiasing; 78 prefs->use_subpixel_positioning = params.subpixel_positioning; 79 prefs->hinting = GetRendererPreferencesHintingEnum(params.hinting); 80 prefs->use_autohinter = params.autohinter; 81 prefs->use_bitmaps = params.use_bitmaps; 82 prefs->subpixel_rendering = 83 GetRendererPreferencesSubpixelRenderingEnum(params.subpixel_rendering); 84 } 85 86 void PopulateFixedWebPreferences(WebPreferences* web_prefs) { 87 web_prefs->shrinks_standalone_images_to_fit = false; 88 web_prefs->should_clear_document_background = false; 89 } 90 91 }; // namespace 92 93 const void* kAwSettingsUserDataKey = &kAwSettingsUserDataKey; 94 95 class AwSettingsUserData : public base::SupportsUserData::Data { 96 public: 97 AwSettingsUserData(AwSettings* ptr) : settings_(ptr) {} 98 99 static AwSettings* GetSettings(content::WebContents* web_contents) { 100 if (!web_contents) 101 return NULL; 102 AwSettingsUserData* data = reinterpret_cast<AwSettingsUserData*>( 103 web_contents->GetUserData(kAwSettingsUserDataKey)); 104 return data ? data->settings_ : NULL; 105 } 106 107 private: 108 AwSettings* settings_; 109 }; 110 111 AwSettings::AwSettings(JNIEnv* env, jobject obj, jlong web_contents) 112 : WebContentsObserver( 113 reinterpret_cast<content::WebContents*>(web_contents)), 114 renderer_prefs_initialized_(false), 115 aw_settings_(env, obj) { 116 reinterpret_cast<content::WebContents*>(web_contents)-> 117 SetUserData(kAwSettingsUserDataKey, new AwSettingsUserData(this)); 118 } 119 120 AwSettings::~AwSettings() { 121 if (web_contents()) { 122 web_contents()->SetUserData(kAwSettingsUserDataKey, NULL); 123 } 124 125 JNIEnv* env = base::android::AttachCurrentThread(); 126 ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env); 127 jobject obj = scoped_obj.obj(); 128 if (!obj) return; 129 Java_AwSettings_nativeAwSettingsGone(env, obj, 130 reinterpret_cast<intptr_t>(this)); 131 } 132 133 void AwSettings::Destroy(JNIEnv* env, jobject obj) { 134 delete this; 135 } 136 137 AwSettings* AwSettings::FromWebContents(content::WebContents* web_contents) { 138 return AwSettingsUserData::GetSettings(web_contents); 139 } 140 141 AwRenderViewHostExt* AwSettings::GetAwRenderViewHostExt() { 142 if (!web_contents()) return NULL; 143 AwContents* contents = AwContents::FromWebContents(web_contents()); 144 if (!contents) return NULL; 145 return contents->render_view_host_ext(); 146 } 147 148 void AwSettings::ResetScrollAndScaleState(JNIEnv* env, jobject obj) { 149 AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt(); 150 if (!rvhe) return; 151 rvhe->ResetScrollAndScaleState(); 152 } 153 154 void AwSettings::UpdateEverything() { 155 JNIEnv* env = base::android::AttachCurrentThread(); 156 CHECK(env); 157 ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env); 158 jobject obj = scoped_obj.obj(); 159 if (!obj) return; 160 // Grab the lock and call UpdateEverythingLocked. 161 Java_AwSettings_updateEverything(env, obj); 162 } 163 164 void AwSettings::UpdateEverythingLocked(JNIEnv* env, jobject obj) { 165 UpdateInitialPageScaleLocked(env, obj); 166 UpdateWebkitPreferencesLocked(env, obj); 167 UpdateUserAgentLocked(env, obj); 168 ResetScrollAndScaleState(env, obj); 169 UpdateFormDataPreferencesLocked(env, obj); 170 UpdateRendererPreferencesLocked(env, obj); 171 } 172 173 void AwSettings::UpdateUserAgentLocked(JNIEnv* env, jobject obj) { 174 if (!web_contents()) return; 175 176 ScopedJavaLocalRef<jstring> str = 177 Java_AwSettings_getUserAgentLocked(env, obj); 178 bool ua_overidden = str.obj() != NULL; 179 180 if (ua_overidden) { 181 std::string override = base::android::ConvertJavaStringToUTF8(str); 182 web_contents()->SetUserAgentOverride(override); 183 } 184 185 const content::NavigationController& controller = 186 web_contents()->GetController(); 187 for (int i = 0; i < controller.GetEntryCount(); ++i) 188 controller.GetEntryAtIndex(i)->SetIsOverridingUserAgent(ua_overidden); 189 } 190 191 void AwSettings::UpdateWebkitPreferencesLocked(JNIEnv* env, jobject obj) { 192 if (!web_contents()) return; 193 AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt(); 194 if (!render_view_host_ext) return; 195 196 content::RenderViewHost* render_view_host = 197 web_contents()->GetRenderViewHost(); 198 if (!render_view_host) return; 199 render_view_host->UpdateWebkitPreferences( 200 render_view_host->GetWebkitPreferences()); 201 } 202 203 void AwSettings::UpdateInitialPageScaleLocked(JNIEnv* env, jobject obj) { 204 AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt(); 205 if (!rvhe) return; 206 207 float initial_page_scale_percent = 208 Java_AwSettings_getInitialPageScalePercentLocked(env, obj); 209 if (initial_page_scale_percent == 0) { 210 rvhe->SetInitialPageScale(-1); 211 } else { 212 float dip_scale = static_cast<float>( 213 Java_AwSettings_getDIPScaleLocked(env, obj)); 214 rvhe->SetInitialPageScale(initial_page_scale_percent / dip_scale / 100.0f); 215 } 216 } 217 218 void AwSettings::UpdateFormDataPreferencesLocked(JNIEnv* env, jobject obj) { 219 if (!web_contents()) return; 220 AwContents* contents = AwContents::FromWebContents(web_contents()); 221 if (!contents) return; 222 223 contents->SetSaveFormData(Java_AwSettings_getSaveFormDataLocked(env, obj)); 224 } 225 226 void AwSettings::UpdateRendererPreferencesLocked(JNIEnv* env, jobject obj) { 227 if (!web_contents()) return; 228 229 bool update_prefs = false; 230 RendererPreferences* prefs = web_contents()->GetMutableRendererPrefs(); 231 232 if (!renderer_prefs_initialized_) { 233 PopulateFixedRendererPreferences(prefs); 234 renderer_prefs_initialized_ = true; 235 update_prefs = true; 236 } 237 238 bool video_overlay = 239 Java_AwSettings_getVideoOverlayForEmbeddedVideoEnabledLocked(env, obj); 240 if (video_overlay != prefs->use_video_overlay_for_embedded_encrypted_video) { 241 prefs->use_video_overlay_for_embedded_encrypted_video = video_overlay; 242 update_prefs = true; 243 } 244 245 content::RenderViewHost* host = web_contents()->GetRenderViewHost(); 246 if (update_prefs && host) 247 host->SyncRendererPrefs(); 248 } 249 250 void AwSettings::RenderViewCreated(content::RenderViewHost* render_view_host) { 251 // A single WebContents can normally have 0 to many RenderViewHost instances 252 // associated with it. 253 // This is important since there is only one RenderViewHostExt instance per 254 // WebContents (and not one RVHExt per RVH, as you might expect) and updating 255 // settings via RVHExt only ever updates the 'current' RVH. 256 // In android_webview we don't swap out the RVH on cross-site navigations, so 257 // we shouldn't have to deal with the multiple RVH per WebContents case. That 258 // in turn means that the newly created RVH is always the 'current' RVH 259 // (since we only ever go from 0 to 1 RVH instances) and hence the DCHECK. 260 DCHECK(web_contents()->GetRenderViewHost() == render_view_host); 261 262 UpdateEverything(); 263 } 264 265 void AwSettings::WebContentsDestroyed() { 266 delete this; 267 } 268 269 void AwSettings::PopulateWebPreferences(WebPreferences* web_prefs) { 270 JNIEnv* env = base::android::AttachCurrentThread(); 271 CHECK(env); 272 ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env); 273 jobject obj = scoped_obj.obj(); 274 if (!obj) return; 275 // Grab the lock and call PopulateWebPreferencesLocked. 276 Java_AwSettings_populateWebPreferences( 277 env, obj, reinterpret_cast<jlong>(web_prefs)); 278 } 279 280 void AwSettings::PopulateWebPreferencesLocked( 281 JNIEnv* env, jobject obj, jlong web_prefs_ptr) { 282 AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt(); 283 if (!render_view_host_ext) return; 284 285 WebPreferences* web_prefs = reinterpret_cast<WebPreferences*>(web_prefs_ptr); 286 PopulateFixedWebPreferences(web_prefs); 287 288 web_prefs->text_autosizing_enabled = 289 Java_AwSettings_getTextAutosizingEnabledLocked(env, obj); 290 291 int text_size_percent = Java_AwSettings_getTextSizePercentLocked(env, obj); 292 if (web_prefs->text_autosizing_enabled) { 293 web_prefs->font_scale_factor = text_size_percent / 100.0f; 294 web_prefs->force_enable_zoom = text_size_percent >= 130; 295 // Use the default zoom factor value when Text Autosizer is turned on. 296 render_view_host_ext->SetTextZoomFactor(1); 297 } else { 298 web_prefs->force_enable_zoom = false; 299 render_view_host_ext->SetTextZoomFactor(text_size_percent / 100.0f); 300 } 301 302 web_prefs->standard_font_family_map[webkit_glue::kCommonScript] = 303 ConvertJavaStringToUTF16( 304 Java_AwSettings_getStandardFontFamilyLocked(env, obj)); 305 306 web_prefs->fixed_font_family_map[webkit_glue::kCommonScript] = 307 ConvertJavaStringToUTF16( 308 Java_AwSettings_getFixedFontFamilyLocked(env, obj)); 309 310 web_prefs->sans_serif_font_family_map[webkit_glue::kCommonScript] = 311 ConvertJavaStringToUTF16( 312 Java_AwSettings_getSansSerifFontFamilyLocked(env, obj)); 313 314 web_prefs->serif_font_family_map[webkit_glue::kCommonScript] = 315 ConvertJavaStringToUTF16( 316 Java_AwSettings_getSerifFontFamilyLocked(env, obj)); 317 318 web_prefs->cursive_font_family_map[webkit_glue::kCommonScript] = 319 ConvertJavaStringToUTF16( 320 Java_AwSettings_getCursiveFontFamilyLocked(env, obj)); 321 322 web_prefs->fantasy_font_family_map[webkit_glue::kCommonScript] = 323 ConvertJavaStringToUTF16( 324 Java_AwSettings_getFantasyFontFamilyLocked(env, obj)); 325 326 web_prefs->default_encoding = ConvertJavaStringToUTF8( 327 Java_AwSettings_getDefaultTextEncodingLocked(env, obj)); 328 329 web_prefs->minimum_font_size = 330 Java_AwSettings_getMinimumFontSizeLocked(env, obj); 331 332 web_prefs->minimum_logical_font_size = 333 Java_AwSettings_getMinimumLogicalFontSizeLocked(env, obj); 334 335 web_prefs->default_font_size = 336 Java_AwSettings_getDefaultFontSizeLocked(env, obj); 337 338 web_prefs->default_fixed_font_size = 339 Java_AwSettings_getDefaultFixedFontSizeLocked(env, obj); 340 341 // Blink's LoadsImagesAutomatically and ImagesEnabled must be 342 // set cris-cross to Android's. See 343 // https://code.google.com/p/chromium/issues/detail?id=224317#c26 344 web_prefs->loads_images_automatically = 345 Java_AwSettings_getImagesEnabledLocked(env, obj); 346 web_prefs->images_enabled = 347 Java_AwSettings_getLoadsImagesAutomaticallyLocked(env, obj); 348 349 web_prefs->javascript_enabled = 350 Java_AwSettings_getJavaScriptEnabledLocked(env, obj); 351 352 web_prefs->allow_universal_access_from_file_urls = 353 Java_AwSettings_getAllowUniversalAccessFromFileURLsLocked(env, obj); 354 355 web_prefs->allow_file_access_from_file_urls = 356 Java_AwSettings_getAllowFileAccessFromFileURLsLocked(env, obj); 357 358 web_prefs->javascript_can_open_windows_automatically = 359 Java_AwSettings_getJavaScriptCanOpenWindowsAutomaticallyLocked(env, obj); 360 361 web_prefs->supports_multiple_windows = 362 Java_AwSettings_getSupportMultipleWindowsLocked(env, obj); 363 364 web_prefs->plugins_enabled = 365 !Java_AwSettings_getPluginsDisabledLocked(env, obj); 366 367 web_prefs->application_cache_enabled = 368 Java_AwSettings_getAppCacheEnabledLocked(env, obj); 369 370 web_prefs->local_storage_enabled = 371 Java_AwSettings_getDomStorageEnabledLocked(env, obj); 372 373 web_prefs->databases_enabled = 374 Java_AwSettings_getDatabaseEnabledLocked(env, obj); 375 376 web_prefs->wide_viewport_quirk = true; 377 web_prefs->use_wide_viewport = 378 Java_AwSettings_getUseWideViewportLocked(env, obj); 379 380 web_prefs->force_zero_layout_height = 381 Java_AwSettings_getForceZeroLayoutHeightLocked(env, obj); 382 383 const bool zero_layout_height_disables_viewport_quirk = 384 Java_AwSettings_getZeroLayoutHeightDisablesViewportQuirkLocked(env, obj); 385 web_prefs->viewport_enabled = !(zero_layout_height_disables_viewport_quirk && 386 web_prefs->force_zero_layout_height); 387 388 web_prefs->double_tap_to_zoom_enabled = 389 Java_AwSettings_supportsDoubleTapZoomLocked(env, obj); 390 391 web_prefs->initialize_at_minimum_page_scale = 392 Java_AwSettings_getLoadWithOverviewModeLocked(env, obj); 393 394 web_prefs->user_gesture_required_for_media_playback = 395 Java_AwSettings_getMediaPlaybackRequiresUserGestureLocked(env, obj); 396 397 ScopedJavaLocalRef<jstring> url = 398 Java_AwSettings_getDefaultVideoPosterURLLocked(env, obj); 399 web_prefs->default_video_poster_url = url.obj() ? 400 GURL(ConvertJavaStringToUTF8(url)) : GURL(); 401 402 bool support_quirks = Java_AwSettings_getSupportLegacyQuirksLocked(env, obj); 403 // Please see the corresponding Blink settings for bug references. 404 web_prefs->support_deprecated_target_density_dpi = support_quirks; 405 web_prefs->use_legacy_background_size_shorthand_behavior = support_quirks; 406 web_prefs->viewport_meta_layout_size_quirk = support_quirks; 407 web_prefs->viewport_meta_merge_content_quirk = support_quirks; 408 web_prefs->viewport_meta_non_user_scalable_quirk = support_quirks; 409 web_prefs->viewport_meta_zero_values_quirk = support_quirks; 410 web_prefs->clobber_user_agent_initial_scale_quirk = support_quirks; 411 web_prefs->ignore_main_frame_overflow_hidden_quirk = support_quirks; 412 web_prefs->report_screen_size_in_physical_pixels_quirk = support_quirks; 413 414 web_prefs->password_echo_enabled = 415 Java_AwSettings_getPasswordEchoEnabledLocked(env, obj); 416 web_prefs->spatial_navigation_enabled = 417 Java_AwSettings_getSpatialNavigationLocked(env, obj); 418 419 bool enable_supported_hardware_accelerated_features = 420 Java_AwSettings_getEnableSupportedHardwareAcceleratedFeaturesLocked( 421 env, obj); 422 423 bool accelerated_2d_canvas_enabled_by_switch = 424 web_prefs->accelerated_2d_canvas_enabled; 425 web_prefs->accelerated_2d_canvas_enabled = true; 426 if (!accelerated_2d_canvas_enabled_by_switch || 427 !enable_supported_hardware_accelerated_features) { 428 // Any canvas smaller than this will fallback to software. Abusing this 429 // slightly to turn canvas off without changing 430 // accelerated_2d_canvas_enabled, which also affects compositing mode. 431 // Using 100M instead of max int to avoid overflows. 432 web_prefs->minimum_accelerated_2d_canvas_size = 100 * 1000 * 1000; 433 } 434 web_prefs->experimental_webgl_enabled = 435 web_prefs->experimental_webgl_enabled && 436 enable_supported_hardware_accelerated_features; 437 438 web_prefs->allow_displaying_insecure_content = 439 Java_AwSettings_getAllowDisplayingInsecureContentLocked(env, obj); 440 web_prefs->allow_running_insecure_content = 441 Java_AwSettings_getAllowRunningInsecureContentLocked(env, obj); 442 443 web_prefs->disallow_fullscreen_for_non_media_elements = true; 444 web_prefs->fullscreen_supported = 445 Java_AwSettings_getFullscreenSupportedLocked(env, obj); 446 } 447 448 static jlong Init(JNIEnv* env, 449 jobject obj, 450 jlong web_contents) { 451 AwSettings* settings = new AwSettings(env, obj, web_contents); 452 return reinterpret_cast<intptr_t>(settings); 453 } 454 455 static jstring GetDefaultUserAgent(JNIEnv* env, jclass clazz) { 456 return base::android::ConvertUTF8ToJavaString(env, GetUserAgent()).Release(); 457 } 458 459 bool RegisterAwSettings(JNIEnv* env) { 460 return RegisterNativesImpl(env); 461 } 462 463 } // namespace android_webview 464