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/native/aw_contents.h"
9#include "base/android/jni_android.h"
10#include "base/android/jni_string.h"
11#include "base/supports_user_data.h"
12#include "content/public/browser/navigation_controller.h"
13#include "content/public/browser/navigation_entry.h"
14#include "content/public/browser/render_view_host.h"
15#include "content/public/browser/web_contents.h"
16#include "content/public/common/content_client.h"
17#include "content/public/common/page_zoom.h"
18#include "jni/AwSettings_jni.h"
19#include "webkit/common/user_agent/user_agent.h"
20#include "webkit/common/webpreferences.h"
21#include "webkit/glue/webkit_glue.h"
22
23using base::android::ConvertJavaStringToUTF16;
24using base::android::ConvertUTF8ToJavaString;
25using base::android::ScopedJavaLocalRef;
26
27namespace android_webview {
28
29const void* kAwSettingsUserDataKey = &kAwSettingsUserDataKey;
30
31class AwSettingsUserData : public base::SupportsUserData::Data {
32 public:
33  AwSettingsUserData(AwSettings* ptr) : settings_(ptr) {}
34
35  static AwSettings* GetSettings(content::WebContents* web_contents) {
36    if (!web_contents)
37      return NULL;
38    AwSettingsUserData* data = reinterpret_cast<AwSettingsUserData*>(
39        web_contents->GetUserData(kAwSettingsUserDataKey));
40    return data ? data->settings_ : NULL;
41  }
42
43 private:
44  AwSettings* settings_;
45};
46
47AwSettings::AwSettings(JNIEnv* env, jobject obj, jint web_contents)
48    : WebContentsObserver(
49          reinterpret_cast<content::WebContents*>(web_contents)),
50      aw_settings_(env, obj) {
51  reinterpret_cast<content::WebContents*>(web_contents)->
52      SetUserData(kAwSettingsUserDataKey, new AwSettingsUserData(this));
53}
54
55AwSettings::~AwSettings() {
56  if (web_contents()) {
57    web_contents()->SetUserData(kAwSettingsUserDataKey, NULL);
58  }
59
60  JNIEnv* env = base::android::AttachCurrentThread();
61  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
62  jobject obj = scoped_obj.obj();
63  if (!obj) return;
64  Java_AwSettings_nativeAwSettingsGone(env, obj,
65                                       reinterpret_cast<jint>(this));
66}
67
68void AwSettings::Destroy(JNIEnv* env, jobject obj) {
69  delete this;
70}
71
72AwSettings* AwSettings::FromWebContents(content::WebContents* web_contents) {
73  return AwSettingsUserData::GetSettings(web_contents);
74}
75
76AwRenderViewHostExt* AwSettings::GetAwRenderViewHostExt() {
77  if (!web_contents()) return NULL;
78  AwContents* contents = AwContents::FromWebContents(web_contents());
79  if (!contents) return NULL;
80  return contents->render_view_host_ext();
81}
82
83void AwSettings::ResetScrollAndScaleState(JNIEnv* env, jobject obj) {
84  AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
85  if (!rvhe) return;
86  rvhe->ResetScrollAndScaleState();
87}
88
89void AwSettings::UpdateEverything() {
90  JNIEnv* env = base::android::AttachCurrentThread();
91  CHECK(env);
92  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
93  jobject obj = scoped_obj.obj();
94  if (!obj) return;
95  // Grab the lock and call UpdateEverythingLocked.
96  Java_AwSettings_updateEverything(env, obj);
97}
98
99void AwSettings::UpdateEverythingLocked(JNIEnv* env, jobject obj) {
100  UpdateInitialPageScaleLocked(env, obj);
101  UpdateWebkitPreferencesLocked(env, obj);
102  UpdateUserAgentLocked(env, obj);
103  ResetScrollAndScaleState(env, obj);
104  UpdateFormDataPreferencesLocked(env, obj);
105}
106
107void AwSettings::UpdateUserAgentLocked(JNIEnv* env, jobject obj) {
108  if (!web_contents()) return;
109
110  ScopedJavaLocalRef<jstring> str =
111      Java_AwSettings_getUserAgentLocked(env, obj);
112  bool ua_overidden = str.obj() != NULL;
113
114  if (ua_overidden) {
115    std::string override = base::android::ConvertJavaStringToUTF8(str);
116    web_contents()->SetUserAgentOverride(override);
117  }
118
119  const content::NavigationController& controller =
120      web_contents()->GetController();
121  for (int i = 0; i < controller.GetEntryCount(); ++i)
122    controller.GetEntryAtIndex(i)->SetIsOverridingUserAgent(ua_overidden);
123}
124
125void AwSettings::UpdateWebkitPreferencesLocked(JNIEnv* env, jobject obj) {
126  if (!web_contents()) return;
127  AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt();
128  if (!render_view_host_ext) return;
129
130  content::RenderViewHost* render_view_host =
131      web_contents()->GetRenderViewHost();
132  if (!render_view_host) return;
133  render_view_host->UpdateWebkitPreferences(
134      render_view_host->GetWebkitPreferences());
135}
136
137void AwSettings::UpdateInitialPageScaleLocked(JNIEnv* env, jobject obj) {
138  AwRenderViewHostExt* rvhe = GetAwRenderViewHostExt();
139  if (!rvhe) return;
140
141  float initial_page_scale_percent =
142      Java_AwSettings_getInitialPageScalePercentLocked(env, obj);
143  if (initial_page_scale_percent == 0) {
144    rvhe->SetInitialPageScale(-1);
145  } else {
146    float dip_scale = static_cast<float>(
147        Java_AwSettings_getDIPScaleLocked(env, obj));
148    rvhe->SetInitialPageScale(initial_page_scale_percent / dip_scale / 100.0f);
149  }
150}
151
152void AwSettings::UpdateFormDataPreferencesLocked(JNIEnv* env, jobject obj) {
153  if (!web_contents()) return;
154  AwContents* contents = AwContents::FromWebContents(web_contents());
155  if (!contents) return;
156
157  contents->SetSaveFormData(Java_AwSettings_getSaveFormDataLocked(env, obj));
158}
159
160void AwSettings::RenderViewCreated(content::RenderViewHost* render_view_host) {
161  // A single WebContents can normally have 0 to many RenderViewHost instances
162  // associated with it.
163  // This is important since there is only one RenderViewHostExt instance per
164  // WebContents (and not one RVHExt per RVH, as you might expect) and updating
165  // settings via RVHExt only ever updates the 'current' RVH.
166  // In android_webview we don't swap out the RVH on cross-site navigations, so
167  // we shouldn't have to deal with the multiple RVH per WebContents case. That
168  // in turn means that the newly created RVH is always the 'current' RVH
169  // (since we only ever go from 0 to 1 RVH instances) and hence the DCHECK.
170  DCHECK(web_contents()->GetRenderViewHost() == render_view_host);
171
172  UpdateEverything();
173}
174
175void AwSettings::WebContentsDestroyed(content::WebContents* web_contents) {
176  delete this;
177}
178
179// static
180void AwSettings::PopulateFixedPreferences(WebPreferences* web_prefs) {
181  web_prefs->shrinks_standalone_images_to_fit = false;
182  web_prefs->should_clear_document_background = false;
183}
184
185void AwSettings::PopulateWebPreferences(WebPreferences* web_prefs) {
186  JNIEnv* env = base::android::AttachCurrentThread();
187  CHECK(env);
188
189  AwRenderViewHostExt* render_view_host_ext = GetAwRenderViewHostExt();
190  if (!render_view_host_ext) return;
191
192  ScopedJavaLocalRef<jobject> scoped_obj = aw_settings_.get(env);
193  jobject obj = scoped_obj.obj();
194  if (!obj) return;
195
196  PopulateFixedPreferences(web_prefs);
197
198  web_prefs->text_autosizing_enabled =
199      Java_AwSettings_getTextAutosizingEnabledLocked(env, obj);
200
201  int text_size_percent = Java_AwSettings_getTextSizePercentLocked(env, obj);
202  if (web_prefs->text_autosizing_enabled) {
203    web_prefs->font_scale_factor = text_size_percent / 100.0f;
204    web_prefs->force_enable_zoom = text_size_percent >= 130;
205    // Use the default zoom level value when Text Autosizer is turned on.
206    render_view_host_ext->SetTextZoomLevel(0);
207  } else {
208    web_prefs->force_enable_zoom = false;
209    render_view_host_ext->SetTextZoomLevel(content::ZoomFactorToZoomLevel(
210        text_size_percent / 100.0f));
211  }
212
213  web_prefs->standard_font_family_map[webkit_glue::kCommonScript] =
214      ConvertJavaStringToUTF16(
215          Java_AwSettings_getStandardFontFamilyLocked(env, obj));
216
217  web_prefs->fixed_font_family_map[webkit_glue::kCommonScript] =
218      ConvertJavaStringToUTF16(
219          Java_AwSettings_getFixedFontFamilyLocked(env, obj));
220
221  web_prefs->sans_serif_font_family_map[webkit_glue::kCommonScript] =
222      ConvertJavaStringToUTF16(
223          Java_AwSettings_getSansSerifFontFamilyLocked(env, obj));
224
225  web_prefs->serif_font_family_map[webkit_glue::kCommonScript] =
226      ConvertJavaStringToUTF16(
227          Java_AwSettings_getSerifFontFamilyLocked(env, obj));
228
229  web_prefs->cursive_font_family_map[webkit_glue::kCommonScript] =
230      ConvertJavaStringToUTF16(
231          Java_AwSettings_getCursiveFontFamilyLocked(env, obj));
232
233  web_prefs->fantasy_font_family_map[webkit_glue::kCommonScript] =
234      ConvertJavaStringToUTF16(
235          Java_AwSettings_getFantasyFontFamilyLocked(env, obj));
236
237  web_prefs->default_encoding = ConvertJavaStringToUTF8(
238      Java_AwSettings_getDefaultTextEncodingLocked(env, obj));
239
240  web_prefs->minimum_font_size =
241      Java_AwSettings_getMinimumFontSizeLocked(env, obj);
242
243  web_prefs->minimum_logical_font_size =
244      Java_AwSettings_getMinimumLogicalFontSizeLocked(env, obj);
245
246  web_prefs->default_font_size =
247      Java_AwSettings_getDefaultFontSizeLocked(env, obj);
248
249  web_prefs->default_fixed_font_size =
250      Java_AwSettings_getDefaultFixedFontSizeLocked(env, obj);
251
252  // Blink's LoadsImagesAutomatically and ImagesEnabled must be
253  // set cris-cross to Android's. See
254  // https://code.google.com/p/chromium/issues/detail?id=224317#c26
255  web_prefs->loads_images_automatically =
256      Java_AwSettings_getImagesEnabledLocked(env, obj);
257  web_prefs->images_enabled =
258      Java_AwSettings_getLoadsImagesAutomaticallyLocked(env, obj);
259
260  web_prefs->javascript_enabled =
261      Java_AwSettings_getJavaScriptEnabledLocked(env, obj);
262
263  web_prefs->allow_universal_access_from_file_urls =
264      Java_AwSettings_getAllowUniversalAccessFromFileURLsLocked(env, obj);
265
266  web_prefs->allow_file_access_from_file_urls =
267      Java_AwSettings_getAllowFileAccessFromFileURLsLocked(env, obj);
268
269  web_prefs->javascript_can_open_windows_automatically =
270      Java_AwSettings_getJavaScriptCanOpenWindowsAutomaticallyLocked(env, obj);
271
272  web_prefs->supports_multiple_windows =
273      Java_AwSettings_getSupportMultipleWindowsLocked(env, obj);
274
275  web_prefs->plugins_enabled =
276      !Java_AwSettings_getPluginsDisabledLocked(env, obj);
277
278  web_prefs->application_cache_enabled =
279      Java_AwSettings_getAppCacheEnabledLocked(env, obj);
280
281  web_prefs->local_storage_enabled =
282      Java_AwSettings_getDomStorageEnabledLocked(env, obj);
283
284  web_prefs->databases_enabled =
285      Java_AwSettings_getDatabaseEnabledLocked(env, obj);
286
287  web_prefs->wide_viewport_quirk = true;
288  web_prefs->double_tap_to_zoom_enabled = web_prefs->use_wide_viewport =
289      Java_AwSettings_getUseWideViewportLocked(env, obj);
290
291  web_prefs->initialize_at_minimum_page_scale =
292      Java_AwSettings_getLoadWithOverviewModeLocked(env, obj);
293
294  web_prefs->user_gesture_required_for_media_playback =
295      Java_AwSettings_getMediaPlaybackRequiresUserGestureLocked(env, obj);
296
297  // Temporary setting, for K release only
298  // This is a fork from upstream chromium, and should go away once
299  // https://code.google.com/p/chromium/issues/detail?id=297216
300  // is implemented
301  web_prefs->allow_running_insecure_content = true;
302
303  ScopedJavaLocalRef<jstring> url =
304      Java_AwSettings_getDefaultVideoPosterURLLocked(env, obj);
305  web_prefs->default_video_poster_url = url.obj() ?
306      GURL(ConvertJavaStringToUTF8(url)) : GURL();
307
308  bool support_quirks = Java_AwSettings_getSupportLegacyQuirksLocked(env, obj);
309  web_prefs->support_deprecated_target_density_dpi = support_quirks;
310  web_prefs->use_legacy_background_size_shorthand_behavior = support_quirks;
311  web_prefs->viewport_meta_layout_size_quirk = support_quirks;
312  web_prefs->viewport_meta_merge_quirk = support_quirks;
313  web_prefs->viewport_meta_zero_values_quirk = support_quirks;
314  web_prefs->ignore_main_frame_overflow_hidden_quirk = support_quirks;
315  web_prefs->report_screen_size_in_physical_pixels_quirk = support_quirks;
316
317  web_prefs->password_echo_enabled =
318      Java_AwSettings_getPasswordEchoEnabledLocked(env, obj);
319  web_prefs->spatial_navigation_enabled =
320      Java_AwSettings_getSpatialNavigationLocked(env, obj);
321}
322
323static jint Init(JNIEnv* env,
324                 jobject obj,
325                 jint web_contents) {
326  AwSettings* settings = new AwSettings(env, obj, web_contents);
327  return reinterpret_cast<jint>(settings);
328}
329
330static jstring GetDefaultUserAgent(JNIEnv* env, jclass clazz) {
331  return base::android::ConvertUTF8ToJavaString(
332      env, content::GetUserAgent(GURL())).Release();
333}
334
335bool RegisterAwSettings(JNIEnv* env) {
336  return RegisterNativesImpl(env) >= 0;
337}
338
339}  // namespace android_webview
340