1/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "websettings"
27
28#include <config.h>
29#include <wtf/Platform.h>
30
31#include "ApplicationCacheStorage.h"
32#include "CString.h"
33#include "DatabaseTracker.h"
34#include "DocLoader.h"
35#include "Document.h"
36#include "EditorClientAndroid.h"
37#include "FileSystem.h"
38#include "Frame.h"
39#include "FrameLoader.h"
40#include "FrameView.h"
41#include "GeolocationPermissions.h"
42#include "GeolocationPositionCache.h"
43#include "Page.h"
44#include "PageCache.h"
45#include "RenderTable.h"
46#include "SQLiteFileSystem.h"
47#include "Settings.h"
48#include "WebCoreFrameBridge.h"
49#include "WebCoreJni.h"
50#if USE(V8)
51#include "WorkerContextExecutionProxy.h"
52#endif
53
54#include <JNIHelp.h>
55#include <utils/misc.h>
56
57namespace android {
58
59static const int permissionFlags660 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
60
61struct FieldIds {
62    FieldIds(JNIEnv* env, jclass clazz) {
63        mLayoutAlgorithm = env->GetFieldID(clazz, "mLayoutAlgorithm",
64                "Landroid/webkit/WebSettings$LayoutAlgorithm;");
65        mTextSize = env->GetFieldID(clazz, "mTextSize",
66                "Landroid/webkit/WebSettings$TextSize;");
67        mStandardFontFamily = env->GetFieldID(clazz, "mStandardFontFamily",
68                "Ljava/lang/String;");
69        mFixedFontFamily = env->GetFieldID(clazz, "mFixedFontFamily",
70                "Ljava/lang/String;");
71        mSansSerifFontFamily = env->GetFieldID(clazz, "mSansSerifFontFamily",
72                "Ljava/lang/String;");
73        mSerifFontFamily = env->GetFieldID(clazz, "mSerifFontFamily",
74                "Ljava/lang/String;");
75        mCursiveFontFamily = env->GetFieldID(clazz, "mCursiveFontFamily",
76                "Ljava/lang/String;");
77        mFantasyFontFamily = env->GetFieldID(clazz, "mFantasyFontFamily",
78                "Ljava/lang/String;");
79        mDefaultTextEncoding = env->GetFieldID(clazz, "mDefaultTextEncoding",
80                "Ljava/lang/String;");
81        mUserAgent = env->GetFieldID(clazz, "mUserAgent",
82                "Ljava/lang/String;");
83        mMinimumFontSize = env->GetFieldID(clazz, "mMinimumFontSize", "I");
84        mMinimumLogicalFontSize = env->GetFieldID(clazz, "mMinimumLogicalFontSize", "I");
85        mDefaultFontSize = env->GetFieldID(clazz, "mDefaultFontSize", "I");
86        mDefaultFixedFontSize = env->GetFieldID(clazz, "mDefaultFixedFontSize", "I");
87        mLoadsImagesAutomatically = env->GetFieldID(clazz, "mLoadsImagesAutomatically", "Z");
88#ifdef ANDROID_BLOCK_NETWORK_IMAGE
89        mBlockNetworkImage = env->GetFieldID(clazz, "mBlockNetworkImage", "Z");
90#endif
91        mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z");
92        mPluginState = env->GetFieldID(clazz, "mPluginState",
93                "Landroid/webkit/WebSettings$PluginState;");
94#if ENABLE(DATABASE)
95        mDatabaseEnabled = env->GetFieldID(clazz, "mDatabaseEnabled", "Z");
96#endif
97#if ENABLE(DOM_STORAGE)
98        mDomStorageEnabled = env->GetFieldID(clazz, "mDomStorageEnabled", "Z");
99#endif
100#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
101        // The databases saved to disk for both the SQL and DOM Storage APIs are stored
102        // in the same base directory.
103        mDatabasePath = env->GetFieldID(clazz, "mDatabasePath", "Ljava/lang/String;");
104        mDatabasePathHasBeenSet = env->GetFieldID(clazz, "mDatabasePathHasBeenSet", "Z");
105#endif
106#if ENABLE(OFFLINE_WEB_APPLICATIONS)
107        mAppCacheEnabled = env->GetFieldID(clazz, "mAppCacheEnabled", "Z");
108        mAppCachePath = env->GetFieldID(clazz, "mAppCachePath", "Ljava/lang/String;");
109        mAppCacheMaxSize = env->GetFieldID(clazz, "mAppCacheMaxSize", "J");
110#endif
111#if ENABLE(WORKERS)
112        mWorkersEnabled = env->GetFieldID(clazz, "mWorkersEnabled", "Z");
113#endif
114        mGeolocationEnabled = env->GetFieldID(clazz, "mGeolocationEnabled", "Z");
115        mGeolocationDatabasePath = env->GetFieldID(clazz, "mGeolocationDatabasePath", "Ljava/lang/String;");
116        mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
117                "mJavaScriptCanOpenWindowsAutomatically", "Z");
118        mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
119        mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z");
120        mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z");
121        mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z");
122        mPageCacheCapacity = env->GetFieldID(clazz, "mPageCacheCapacity", "I");
123
124        LOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm");
125        LOG_ASSERT(mTextSize, "Could not find field mTextSize");
126        LOG_ASSERT(mStandardFontFamily, "Could not find field mStandardFontFamily");
127        LOG_ASSERT(mFixedFontFamily, "Could not find field mFixedFontFamily");
128        LOG_ASSERT(mSansSerifFontFamily, "Could not find field mSansSerifFontFamily");
129        LOG_ASSERT(mSerifFontFamily, "Could not find field mSerifFontFamily");
130        LOG_ASSERT(mCursiveFontFamily, "Could not find field mCursiveFontFamily");
131        LOG_ASSERT(mFantasyFontFamily, "Could not find field mFantasyFontFamily");
132        LOG_ASSERT(mDefaultTextEncoding, "Could not find field mDefaultTextEncoding");
133        LOG_ASSERT(mUserAgent, "Could not find field mUserAgent");
134        LOG_ASSERT(mMinimumFontSize, "Could not find field mMinimumFontSize");
135        LOG_ASSERT(mMinimumLogicalFontSize, "Could not find field mMinimumLogicalFontSize");
136        LOG_ASSERT(mDefaultFontSize, "Could not find field mDefaultFontSize");
137        LOG_ASSERT(mDefaultFixedFontSize, "Could not find field mDefaultFixedFontSize");
138        LOG_ASSERT(mLoadsImagesAutomatically, "Could not find field mLoadsImagesAutomatically");
139#ifdef ANDROID_BLOCK_NETWORK_IMAGE
140        LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage");
141#endif
142        LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
143        LOG_ASSERT(mPluginState, "Could not find field mPluginState");
144#if ENABLE(OFFLINE_WEB_APPLICATIONS)
145        LOG_ASSERT(mAppCacheEnabled, "Could not find field mAppCacheEnabled");
146        LOG_ASSERT(mAppCachePath, "Could not find field mAppCachePath");
147        LOG_ASSERT(mAppCacheMaxSize, "Could not find field mAppCacheMaxSize");
148#endif
149#if ENABLE(WORKERS)
150        LOG_ASSERT(mWorkersEnabled, "Could not find field mWorkersEnabled");
151#endif
152        LOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically,
153                "Could not find field mJavaScriptCanOpenWindowsAutomatically");
154        LOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport");
155        LOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows");
156        LOG_ASSERT(mShrinksStandaloneImagesToFit, "Could not find field mShrinksStandaloneImagesToFit");
157        LOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree");
158        LOG_ASSERT(mPageCacheCapacity, "Could not find field mPageCacheCapacity");
159
160        jclass c = env->FindClass("java/lang/Enum");
161        LOG_ASSERT(c, "Could not find Enum class!");
162        mOrdinal = env->GetMethodID(c, "ordinal", "()I");
163        LOG_ASSERT(mOrdinal, "Could not find method ordinal");
164        c = env->FindClass("android/webkit/WebSettings$TextSize");
165        LOG_ASSERT(c, "Could not find TextSize enum");
166        mTextSizeValue = env->GetFieldID(c, "value", "I");
167    }
168
169    // Field ids
170    jfieldID mLayoutAlgorithm;
171    jfieldID mTextSize;
172    jfieldID mStandardFontFamily;
173    jfieldID mFixedFontFamily;
174    jfieldID mSansSerifFontFamily;
175    jfieldID mSerifFontFamily;
176    jfieldID mCursiveFontFamily;
177    jfieldID mFantasyFontFamily;
178    jfieldID mDefaultTextEncoding;
179    jfieldID mUserAgent;
180    jfieldID mMinimumFontSize;
181    jfieldID mMinimumLogicalFontSize;
182    jfieldID mDefaultFontSize;
183    jfieldID mDefaultFixedFontSize;
184    jfieldID mLoadsImagesAutomatically;
185#ifdef ANDROID_BLOCK_NETWORK_IMAGE
186    jfieldID mBlockNetworkImage;
187#endif
188    jfieldID mJavaScriptEnabled;
189    jfieldID mPluginState;
190#if ENABLE(OFFLINE_WEB_APPLICATIONS)
191    jfieldID mAppCacheEnabled;
192    jfieldID mAppCachePath;
193    jfieldID mAppCacheMaxSize;
194#endif
195#if ENABLE(WORKERS)
196    jfieldID mWorkersEnabled;
197#endif
198    jfieldID mJavaScriptCanOpenWindowsAutomatically;
199    jfieldID mUseWideViewport;
200    jfieldID mSupportMultipleWindows;
201    jfieldID mShrinksStandaloneImagesToFit;
202    jfieldID mUseDoubleTree;
203    jfieldID mPageCacheCapacity;
204    // Ordinal() method and value field for enums
205    jmethodID mOrdinal;
206    jfieldID  mTextSizeValue;
207
208#if ENABLE(DATABASE)
209    jfieldID mDatabaseEnabled;
210#endif
211#if ENABLE(DOM_STORAGE)
212    jfieldID mDomStorageEnabled;
213#endif
214    jfieldID mGeolocationEnabled;
215    jfieldID mGeolocationDatabasePath;
216#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
217    jfieldID mDatabasePath;
218    jfieldID mDatabasePathHasBeenSet;
219#endif
220};
221
222static struct FieldIds* gFieldIds;
223
224// Note: This is moved from the old FrameAndroid.cpp
225static void recursiveCleanupForFullLayout(WebCore::RenderObject* obj)
226{
227    obj->setNeedsLayout(true, false);
228#ifdef ANDROID_LAYOUT
229    if (obj->isTable())
230        (static_cast<WebCore::RenderTable *>(obj))->clearSingleColumn();
231#endif
232    for (WebCore::RenderObject* n = obj->firstChild(); n; n = n->nextSibling())
233        recursiveCleanupForFullLayout(n);
234}
235
236class WebSettings {
237public:
238    static void Sync(JNIEnv* env, jobject obj, jint frame)
239    {
240        WebCore::Frame* pFrame = (WebCore::Frame*)frame;
241        LOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__);
242        WebCore::Settings* s = pFrame->settings();
243        if (!s)
244            return;
245        WebCore::DocLoader* docLoader = pFrame->document()->docLoader();
246
247#ifdef ANDROID_LAYOUT
248        jobject layout = env->GetObjectField(obj, gFieldIds->mLayoutAlgorithm);
249        WebCore::Settings::LayoutAlgorithm l = (WebCore::Settings::LayoutAlgorithm)
250                env->CallIntMethod(layout, gFieldIds->mOrdinal);
251        if (s->layoutAlgorithm() != l) {
252            s->setLayoutAlgorithm(l);
253            if (pFrame->document()) {
254                pFrame->document()->updateStyleSelector();
255                if (pFrame->document()->renderer()) {
256                    recursiveCleanupForFullLayout(pFrame->document()->renderer());
257                    LOG_ASSERT(pFrame->view(), "No view for this frame when trying to relayout");
258                    pFrame->view()->layout();
259                    // FIXME: This call used to scroll the page to put the focus into view.
260                    // It worked on the WebViewCore, but now scrolling is done outside of the
261                    // WebViewCore, on the UI side, so there needs to be a new way to do this.
262                    //pFrame->makeFocusVisible();
263                }
264            }
265        }
266#endif
267        jobject textSize = env->GetObjectField(obj, gFieldIds->mTextSize);
268        float zoomFactor = env->GetIntField(textSize, gFieldIds->mTextSizeValue) / 100.0f;
269        if (pFrame->zoomFactor() != zoomFactor)
270            pFrame->setZoomFactor(zoomFactor, /*isTextOnly*/true);
271
272        jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily);
273        s->setStandardFontFamily(to_string(env, str));
274
275        str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily);
276        s->setFixedFontFamily(to_string(env, str));
277
278        str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily);
279        s->setSansSerifFontFamily(to_string(env, str));
280
281        str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily);
282        s->setSerifFontFamily(to_string(env, str));
283
284        str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily);
285        s->setCursiveFontFamily(to_string(env, str));
286
287        str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily);
288        s->setFantasyFontFamily(to_string(env, str));
289
290        str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding);
291        s->setDefaultTextEncodingName(to_string(env, str));
292
293        str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent);
294        WebFrame::getWebFrame(pFrame)->setUserAgent(to_string(env, str));
295
296        jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize);
297        s->setMinimumFontSize(size);
298
299        size = env->GetIntField(obj, gFieldIds->mMinimumLogicalFontSize);
300        s->setMinimumLogicalFontSize(size);
301
302        size = env->GetIntField(obj, gFieldIds->mDefaultFontSize);
303        s->setDefaultFontSize(size);
304
305        size = env->GetIntField(obj, gFieldIds->mDefaultFixedFontSize);
306        s->setDefaultFixedFontSize(size);
307
308        jboolean flag = env->GetBooleanField(obj, gFieldIds->mLoadsImagesAutomatically);
309        s->setLoadsImagesAutomatically(flag);
310        if (flag)
311            docLoader->setAutoLoadImages(true);
312
313#ifdef ANDROID_BLOCK_NETWORK_IMAGE
314        flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage);
315        s->setBlockNetworkImage(flag);
316        if(!flag)
317            docLoader->setBlockNetworkImage(false);
318#endif
319
320        flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled);
321        s->setJavaScriptEnabled(flag);
322
323        // ON = 0
324        // ON_DEMAND = 1
325        // OFF = 2
326        jobject pluginState = env->GetObjectField(obj, gFieldIds->mPluginState);
327        int state = env->CallIntMethod(pluginState, gFieldIds->mOrdinal);
328        s->setPluginsEnabled(state < 2);
329#ifdef ANDROID_PLUGINS
330        s->setPluginsOnDemand(state == 1);
331#endif
332
333#if ENABLE(OFFLINE_WEB_APPLICATIONS)
334        flag = env->GetBooleanField(obj, gFieldIds->mAppCacheEnabled);
335        s->setOfflineWebApplicationCacheEnabled(flag);
336        str = (jstring)env->GetObjectField(obj, gFieldIds->mAppCachePath);
337        if (str) {
338            WebCore::String path = to_string(env, str);
339            if (path.length() && WebCore::cacheStorage().cacheDirectory().isNull()) {
340                WebCore::cacheStorage().setCacheDirectory(path);
341                // This database is created on the first load. If the file
342                // doesn't exist, we create it and set its permissions. The
343                // filename must match that in ApplicationCacheStorage.cpp.
344                String filename = pathByAppendingComponent(path, "ApplicationCache.db");
345                int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
346                if (fd >= 0)
347                    close(fd);
348            }
349        }
350        jlong maxsize = env->GetIntField(obj, gFieldIds->mAppCacheMaxSize);
351        WebCore::cacheStorage().setMaximumSize(maxsize);
352#endif
353
354        flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptCanOpenWindowsAutomatically);
355        s->setJavaScriptCanOpenWindowsAutomatically(flag);
356
357#ifdef ANDROID_LAYOUT
358        flag = env->GetBooleanField(obj, gFieldIds->mUseWideViewport);
359        s->setUseWideViewport(flag);
360#endif
361
362#ifdef ANDROID_MULTIPLE_WINDOWS
363        flag = env->GetBooleanField(obj, gFieldIds->mSupportMultipleWindows);
364        s->setSupportMultipleWindows(flag);
365#endif
366        flag = env->GetBooleanField(obj, gFieldIds->mShrinksStandaloneImagesToFit);
367        s->setShrinksStandaloneImagesToFit(flag);
368#if ENABLE(DATABASE)
369        flag = env->GetBooleanField(obj, gFieldIds->mDatabaseEnabled);
370        s->setDatabasesEnabled(flag);
371
372        flag = env->GetBooleanField(obj, gFieldIds->mDatabasePathHasBeenSet);
373        if (flag) {
374            // If the user has set the database path, sync it to the DatabaseTracker.
375            str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
376            if (str) {
377                String path = to_string(env, str);
378                WebCore::DatabaseTracker::tracker().setDatabaseDirectoryPath(path);
379                // This database is created when the first HTML5 Database object is
380                // instantiated. If the file doesn't exist, we create it and set its
381                // permissions. The filename must match that in
382                // DatabaseTracker.cpp.
383                String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(path, "Databases.db");
384                int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
385                if (fd >= 0)
386                    close(fd);
387            }
388        }
389#endif
390#if ENABLE(DOM_STORAGE)
391        flag = env->GetBooleanField(obj, gFieldIds->mDomStorageEnabled);
392        s->setLocalStorageEnabled(flag);
393        str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
394        if (str) {
395            WebCore::String localStorageDatabasePath = to_string(env,str);
396            if (localStorageDatabasePath.length()) {
397                localStorageDatabasePath = WebCore::pathByAppendingComponent(
398                        localStorageDatabasePath, "localstorage");
399                // We need 770 for folders
400                mkdir(localStorageDatabasePath.utf8().data(),
401                        permissionFlags660 | S_IXUSR | S_IXGRP);
402                s->setLocalStorageDatabasePath(localStorageDatabasePath);
403            }
404        }
405#endif
406
407        flag = env->GetBooleanField(obj, gFieldIds->mGeolocationEnabled);
408        GeolocationPermissions::setAlwaysDeny(!flag);
409        str = (jstring)env->GetObjectField(obj, gFieldIds->mGeolocationDatabasePath);
410        if (str) {
411            WebCore::String path = to_string(env, str);
412            GeolocationPermissions::setDatabasePath(path);
413            WebCore::GeolocationPositionCache::setDatabasePath(path);
414            // This database is created when the first Geolocation object is
415            // instantiated. If the file doesn't exist, we create it and set its
416            // permissions. The filename must match that in
417            // GeolocationPositionCache.cpp.
418            WebCore::String filename = WebCore::SQLiteFileSystem::appendDatabaseFileNameToPath(
419                    path, "CachedGeoposition.db");
420            int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
421            if (fd >= 0)
422                close(fd);
423        }
424
425        size = env->GetIntField(obj, gFieldIds->mPageCacheCapacity);
426        if (size > 0) {
427            s->setUsesPageCache(true);
428            WebCore::pageCache()->setCapacity(size);
429        } else
430            s->setUsesPageCache(false);
431    }
432};
433
434//-------------------------------------------------------------
435// JNI registration
436//-------------------------------------------------------------
437
438static JNINativeMethod gWebSettingsMethods[] = {
439    { "nativeSync", "(I)V",
440        (void*) WebSettings::Sync }
441};
442
443int register_websettings(JNIEnv* env)
444{
445    jclass clazz = env->FindClass("android/webkit/WebSettings");
446    LOG_ASSERT(clazz, "Unable to find class WebSettings!");
447    gFieldIds = new FieldIds(env, clazz);
448    return jniRegisterNativeMethods(env, "android/webkit/WebSettings",
449            gWebSettingsMethods, NELEM(gWebSettingsMethods));
450}
451
452}
453