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.
GetRendererPreferencesHintingEnum(gfx::FontRenderParams::Hinting hinting)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
GetRendererPreferencesSubpixelRenderingEnum(gfx::FontRenderParams::SubpixelRendering subpixel_rendering)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
PopulateFixedRendererPreferences(RendererPreferences * prefs)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
PopulateFixedWebPreferences(WebPreferences * web_prefs)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:
AwSettingsUserData(AwSettings * ptr)97 AwSettingsUserData(AwSettings* ptr) : settings_(ptr) {}
98
GetSettings(content::WebContents * web_contents)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
AwSettings(JNIEnv * env,jobject obj,jlong web_contents)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
~AwSettings()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
Destroy(JNIEnv * env,jobject obj)133 void AwSettings::Destroy(JNIEnv* env, jobject obj) {
134 delete this;
135 }
136
FromWebContents(content::WebContents * web_contents)137 AwSettings* AwSettings::FromWebContents(content::WebContents* web_contents) {
138 return AwSettingsUserData::GetSettings(web_contents);
139 }
140
GetAwRenderViewHostExt()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
ResetScrollAndScaleState(JNIEnv * env,jobject obj)148 void AwSettings::ResetScrollAndScaleState(JNIEnv* env, jobject obj) {
149 AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
150 if (!rvhe) return;
151 rvhe->ResetScrollAndScaleState();
152 }
153
UpdateEverything()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
UpdateEverythingLocked(JNIEnv * env,jobject obj)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
UpdateUserAgentLocked(JNIEnv * env,jobject obj)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
UpdateWebkitPreferencesLocked(JNIEnv * env,jobject obj)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
UpdateInitialPageScaleLocked(JNIEnv * env,jobject obj)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
UpdateFormDataPreferencesLocked(JNIEnv * env,jobject obj)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
UpdateRendererPreferencesLocked(JNIEnv * env,jobject obj)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
RenderViewCreated(content::RenderViewHost * render_view_host)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
WebContentsDestroyed()265 void AwSettings::WebContentsDestroyed() {
266 delete this;
267 }
268
PopulateWebPreferences(WebPreferences * web_prefs)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
PopulateWebPreferencesLocked(JNIEnv * env,jobject obj,jlong web_prefs_ptr)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
Init(JNIEnv * env,jobject obj,jlong web_contents)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
GetDefaultUserAgent(JNIEnv * env,jclass clazz)455 static jstring GetDefaultUserAgent(JNIEnv* env, jclass clazz) {
456 return base::android::ConvertUTF8ToJavaString(env, GetUserAgent()).Release();
457 }
458
RegisterAwSettings(JNIEnv * env)459 bool RegisterAwSettings(JNIEnv* env) {
460 return RegisterNativesImpl(env);
461 }
462
463 } // namespace android_webview
464