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