Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkContent.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013-2014 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 package org.xwalk.core.internal;
7
8 import android.app.Activity;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.SharedPreferences;
12 import android.graphics.Rect;
13 import android.os.Build.VERSION;
14 import android.os.Build.VERSION_CODES;
15 import android.os.Bundle;
16 import android.view.View;
17 import android.view.WindowManager;
18 import android.text.TextUtils;
19 import android.util.AttributeSet;
20 import android.util.Log;
21 import android.view.ViewGroup;
22 import android.webkit.ValueCallback;
23 import android.webkit.WebResourceResponse;
24 import android.widget.FrameLayout;
25
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.lang.annotation.Annotation;
29
30 import org.chromium.base.CalledByNative;
31 import org.chromium.base.JNINamespace;
32 import org.chromium.base.ThreadUtils;
33 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
34 import org.chromium.content.browser.ContentView;
35 import org.chromium.content.browser.ContentViewCore;
36 import org.chromium.content.browser.ContentViewRenderView;
37 import org.chromium.content.browser.ContentViewRenderView.CompositingSurfaceType;
38 import org.chromium.content.browser.ContentViewStatics;
39 import org.chromium.content.browser.LoadUrlParams;
40 import org.chromium.content.browser.NavigationHistory;
41 import org.chromium.content.common.CleanupReference;
42 import org.chromium.content_public.browser.JavaScriptCallback;
43 import org.chromium.media.MediaPlayerBridge;
44 import org.chromium.ui.base.ActivityWindowAndroid;
45 import org.chromium.ui.gfx.DeviceDisplayInfo;
46
47 @JNINamespace("xwalk")
48 /**
49  * This class is the implementation class for XWalkViewInternal by calling internal
50  * various classes.
51  */
52 class XWalkContent extends FrameLayout implements XWalkPreferencesInternal.KeyValueChangeListener {
53     private static String TAG = "XWalkContent";
54     private static Class<? extends Annotation> javascriptInterfaceClass = null;
55
56     private ContentViewCore mContentViewCore;
57     private ContentView mContentView;
58     private ContentViewRenderView mContentViewRenderView;
59     private ActivityWindowAndroid mWindow;
60     private XWalkDevToolsServer mDevToolsServer;
61     private XWalkViewInternal mXWalkView;
62     private XWalkContentsClientBridge mContentsClientBridge;
63     private XWalkContentsIoThreadClient mIoThreadClient;
64     private XWalkWebContentsDelegateAdapter mXWalkContentsDelegateAdapter;
65     private XWalkSettings mSettings;
66     private XWalkGeolocationPermissions mGeolocationPermissions;
67     private XWalkLaunchScreenManager mLaunchScreenManager;
68
69     long mXWalkContent;
70     long mWebContents;
71
72     static void setJavascriptInterfaceClass(Class<? extends Annotation> clazz) {
73       assert(javascriptInterfaceClass == null);
74       javascriptInterfaceClass = clazz;
75     }
76
77     private static final class DestroyRunnable implements Runnable {
78         private final long mXWalkContent;
79         private DestroyRunnable(long nativeXWalkContent) {
80             mXWalkContent = nativeXWalkContent;
81         }
82
83         @Override
84         public void run() {
85             nativeDestroy(mXWalkContent);
86         }
87     }
88
89     // Reference to the active mXWalkContent pointer while it is active use
90     // (ie before it is destroyed).
91     private CleanupReference mCleanupReference;
92
93     public XWalkContent(Context context, AttributeSet attrs, XWalkViewInternal xwView) {
94         super(context, attrs);
95
96         // Initialize the WebContensDelegate.
97         mXWalkView = xwView;
98         mContentsClientBridge = new XWalkContentsClientBridge(mXWalkView);
99         mXWalkContentsDelegateAdapter = new XWalkWebContentsDelegateAdapter(
100             mContentsClientBridge);
101         mIoThreadClient = new XWalkIoThreadClientImpl();
102
103         // Initialize mWindow which is needed by content
104         mWindow = new ActivityWindowAndroid(xwView.getActivity());
105
106         // Initialize ContentViewRenderView
107         boolean animated = XWalkPreferencesInternal.getValue(XWalkPreferencesInternal.ANIMATABLE_XWALK_VIEW);
108         CompositingSurfaceType surfaceType =
109                 animated ? CompositingSurfaceType.TEXTURE_VIEW : CompositingSurfaceType.SURFACE_VIEW;
110         mContentViewRenderView = new ContentViewRenderView(context, surfaceType) {
111             protected void onReadyToRender() {
112                 // Anything depending on the underlying Surface readiness should
113                 // be placed here.
114             }
115         };
116         mContentViewRenderView.onNativeLibraryLoaded(mWindow);
117         mLaunchScreenManager = new XWalkLaunchScreenManager(context, mXWalkView);
118         mContentViewRenderView.registerFirstRenderedFrameListener(mLaunchScreenManager);
119         addView(mContentViewRenderView,
120                 new FrameLayout.LayoutParams(
121                         FrameLayout.LayoutParams.MATCH_PARENT,
122                         FrameLayout.LayoutParams.MATCH_PARENT));
123
124         mXWalkContent = nativeInit(mXWalkContentsDelegateAdapter, mContentsClientBridge);
125
126         // The native side object has been bound to this java instance, so now is the time to
127         // bind all the native->java relationships.
128         mCleanupReference = new CleanupReference(this, new DestroyRunnable(mXWalkContent));
129
130         mWebContents = nativeGetWebContents(mXWalkContent, mIoThreadClient,
131                 mContentsClientBridge.getInterceptNavigationDelegate());
132
133         // Initialize ContentView.
134         mContentViewCore = new ContentViewCore(getContext());
135         mContentView = ContentView.newInstance(getContext(), mContentViewCore);
136         mContentViewCore.initialize(mContentView, mContentView, mWebContents, mWindow);
137         addView(mContentView,
138                 new FrameLayout.LayoutParams(
139                         FrameLayout.LayoutParams.MATCH_PARENT,
140                         FrameLayout.LayoutParams.MATCH_PARENT));
141         mContentViewCore.setContentViewClient(mContentsClientBridge);
142         mContentViewRenderView.setCurrentContentViewCore(mContentViewCore);
143         // For addJavascriptInterface
144         mContentsClientBridge.installWebContentsObserver(mContentViewCore.getWebContents());
145
146         // Set DIP scale.
147         mContentsClientBridge.setDIPScale(DeviceDisplayInfo.create(context).getDIPScale());
148
149         mContentViewCore.setDownloadDelegate(mContentsClientBridge);
150
151         // Set the third argument isAccessFromFileURLsGrantedByDefault to false, so that
152         // the members mAllowUniversalAccessFromFileURLs and mAllowFileAccessFromFileURLs
153         // won't be changed from false to true at the same time in the constructor of
154         // XWalkSettings class.
155         mSettings = new XWalkSettings(getContext(), mWebContents, false);
156         // Enable AllowFileAccessFromFileURLs, so that files under file:// path could be
157         // loaded by XMLHttpRequest.
158         mSettings.setAllowFileAccessFromFileURLs(true);
159
160         SharedPreferences sharedPreferences = new InMemorySharedPreferences();
161         mGeolocationPermissions = new XWalkGeolocationPermissions(sharedPreferences);
162
163         MediaPlayerBridge.setResourceLoadingFilter(
164                 new XWalkMediaPlayerResourceLoadingFilter());
165
166         XWalkPreferencesInternal.load(this);
167     }
168
169     void doLoadUrl(String url, String content) {
170         // Handle the same url loading by parameters.
171         if (url != null && !url.isEmpty() &&
172                 TextUtils.equals(url, mContentViewCore.getUrl())) {
173             mContentViewCore.reload(true);
174         } else {
175             LoadUrlParams params = null;
176             if (content == null || content.isEmpty()) {
177                 params = new LoadUrlParams(url);
178             } else {
179                 params = LoadUrlParams.createLoadDataParamsWithBaseUrl(
180                         content, "text/html", false, url, null);
181             }
182             params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
183             mContentViewCore.loadUrl(params);
184         }
185
186         mContentView.requestFocus();
187     }
188
189     public void loadUrl(String url, String data) {
190         if ((url == null || url.isEmpty()) &&
191                 (data == null || data.isEmpty())) {
192             return;
193         }
194
195         doLoadUrl(url, data);
196     }
197
198     public void reload(int mode) {
199         switch (mode) {
200             case XWalkViewInternal.RELOAD_IGNORE_CACHE:
201                 mContentViewCore.reloadIgnoringCache(true);
202                 break;
203             case XWalkViewInternal.RELOAD_NORMAL:
204             default:
205                 mContentViewCore.reload(true);
206         }
207     }
208
209     public String getUrl() {
210         String url = mContentViewCore.getUrl();
211         if (url == null || url.trim().isEmpty()) return null;
212         return url;
213     }
214
215     public String getTitle() {
216         String title = mContentViewCore.getTitle().trim();
217         if (title == null) title = "";
218         return title;
219     }
220
221     public void addJavascriptInterface(Object object, String name) {
222         mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name,
223                 javascriptInterfaceClass);
224     }
225
226     public void evaluateJavascript(String script, ValueCallback<String> callback) {
227         final ValueCallback<String>  fCallback = callback;
228         JavaScriptCallback coreCallback = null;
229         if (fCallback != null) {
230             coreCallback = new JavaScriptCallback() {
231                 @Override
232                 public void handleJavaScriptResult(String jsonResult) {
233                     fCallback.onReceiveValue(jsonResult);
234                 }
235             };
236         }
237         mContentViewCore.evaluateJavaScript(script, coreCallback);
238     }
239
240     public void setUIClient(XWalkUIClientInternal client) {
241         mContentsClientBridge.setUIClient(client);
242     }
243
244     public void setResourceClient(XWalkResourceClientInternal client) {
245         mContentsClientBridge.setResourceClient(client);
246     }
247
248     public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
249         mContentsClientBridge.setXWalkWebChromeClient(client);
250     }
251
252     public XWalkWebChromeClient getXWalkWebChromeClient() {
253         return mContentsClientBridge.getXWalkWebChromeClient();
254     }
255
256     public void setXWalkClient(XWalkClient client) {
257         mContentsClientBridge.setXWalkClient(client);
258     }
259
260     public void setDownloadListener(DownloadListener listener) {
261         mContentsClientBridge.setDownloadListener(listener);
262     }
263
264     public void setNavigationHandler(XWalkNavigationHandler handler) {
265         mContentsClientBridge.setNavigationHandler(handler);
266     }
267
268     public void setNotificationService(XWalkNotificationService service) {
269         mContentsClientBridge.setNotificationService(service);
270     }
271
272     public void onPause() {
273         mContentViewCore.onHide();
274     }
275
276     public void onResume() {
277         mContentViewCore.onShow();
278     }
279
280     public void onActivityResult(int requestCode, int resultCode, Intent data) {
281         mWindow.onActivityResult(requestCode, resultCode, data);
282     }
283
284     public boolean onNewIntent(Intent intent) {
285         return mContentsClientBridge.onNewIntent(intent);
286     }
287
288     public void clearCache(boolean includeDiskFiles) {
289         if (mXWalkContent == 0) return;
290         nativeClearCache(mXWalkContent, includeDiskFiles);
291     }
292
293     public void clearHistory() {
294         mContentViewCore.clearHistory();
295     }
296
297     public boolean canGoBack() {
298         return mContentViewCore.canGoBack();
299     }
300
301     public void goBack() {
302         mContentViewCore.goBack();
303     }
304
305     public boolean canGoForward() {
306         return mContentViewCore.canGoForward();
307     }
308
309     public void goForward() {
310         mContentViewCore.goForward();
311     }
312
313     void navigateTo(int offset)  {
314         mContentViewCore.goToOffset(offset);
315     }
316
317     public void stopLoading() {
318         mContentViewCore.stopLoading();
319         mContentsClientBridge.onStopLoading();
320     }
321
322     // Currently, timer pause/resume is actually
323     // a global setting. And multiple pause will fail the
324     // DCHECK in content (content_view_statics.cc:57).
325     // Here uses a static boolean to avoid this issue.
326     private static boolean timerPaused = false;
327
328     // TODO(Guangzhen): ContentViewStatics will be removed in upstream,
329     // details in content_view_statics.cc.
330     // We need follow up after upstream updates that.
331     public void pauseTimers() {
332         if (timerPaused) return;
333         ContentViewStatics.setWebKitSharedTimersSuspended(true);
334         timerPaused = true;
335     }
336
337     public void resumeTimers() {
338         if (!timerPaused) return;
339         ContentViewStatics.setWebKitSharedTimersSuspended(false);
340         timerPaused = false;
341     }
342
343     public String getOriginalUrl() {
344         NavigationHistory history = mContentViewCore.getNavigationHistory();
345         int currentIndex = history.getCurrentEntryIndex();
346         if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
347             return history.getEntryAtIndex(currentIndex).getOriginalUrl();
348         }
349         return null;
350     }
351
352     public String getXWalkVersion() {
353         if (mXWalkContent == 0) return "";
354         return nativeGetVersion(mXWalkContent);
355     }
356
357     public void setNetworkAvailable(boolean networkUp) {
358         if (mXWalkContent == 0) return;
359         nativeSetJsOnlineProperty(mXWalkContent, networkUp);
360     }
361
362     // For instrumentation test.
363     public ContentViewCore getContentViewCoreForTest() {
364         return mContentViewCore;
365     }
366
367     // For instrumentation test.
368     public void installWebContentsObserverForTest(XWalkContentsClient contentClient) {
369         contentClient.installWebContentsObserver(mContentViewCore.getWebContents());
370     }
371
372     public String devToolsAgentId() {
373         if (mXWalkContent == 0) return "";
374         return nativeDevToolsAgentId(mXWalkContent);
375     }
376
377     public XWalkSettings getSettings() {
378         return mSettings;
379     }
380
381     public void loadAppFromManifest(String url, String data) {
382         if (mXWalkContent == 0 ||
383                 ((url == null || url.isEmpty()) &&
384                         (data == null || data.isEmpty()))) {
385             return;
386         }
387
388         String content = data;
389         // If the data of manifest.json is not set, try to load it.
390         if (data == null || data.isEmpty()) {
391             try {
392                 content = AndroidProtocolHandler.getUrlContent(mXWalkView.getActivity(), url);
393             } catch (IOException e) {
394                 throw new RuntimeException("Failed to read the manifest: " + url);
395             }
396         }
397
398         // Calculate the base url of manifestUrl. Used by native side.
399         // TODO(yongsheng): It's from runtime side. Need to find a better way
400         // to get base url.
401         String baseUrl = url;
402         int position = url.lastIndexOf("/");
403         if (position != -1) {
404             baseUrl = url.substring(0, position + 1);
405         } else {
406             Log.w(TAG, "The url of manifest.json is probably not set correctly.");
407         }
408
409         if (!nativeSetManifest(mXWalkContent, baseUrl, content)) {
410             throw new RuntimeException("Failed to parse the manifest file: " + url);
411         }
412     }
413
414     public XWalkNavigationHistoryInternal getNavigationHistory() {
415         return new XWalkNavigationHistoryInternal(mXWalkView, mContentViewCore.getNavigationHistory());
416     }
417
418     public static final String SAVE_RESTORE_STATE_KEY = "XWALKVIEW_STATE";
419
420     public XWalkNavigationHistoryInternal saveState(Bundle outState) {
421         if (outState == null) return null;
422
423         byte[] state = nativeGetState(mXWalkContent);
424         if (state == null) return null;
425
426         outState.putByteArray(SAVE_RESTORE_STATE_KEY, state);
427         return getNavigationHistory();
428     }
429
430     public XWalkNavigationHistoryInternal restoreState(Bundle inState) {
431         if (inState == null) return null;
432
433         byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
434         if (state == null) return null;
435
436         boolean result = nativeSetState(mXWalkContent, state);
437
438         // The onUpdateTitle callback normally happens when a page is loaded,
439         // but is optimized out in the restoreState case because the title is
440         // already restored. See WebContentsImpl::UpdateTitleForEntry. So we
441         // call the callback explicitly here.
442         if (result) {
443             mContentsClientBridge.onUpdateTitle(mContentViewCore.getTitle());
444         }
445
446         return result ? getNavigationHistory() : null;
447     }
448
449     boolean hasEnteredFullscreen() {
450         return mContentsClientBridge.hasEnteredFullscreen();
451     }
452
453     void exitFullscreen() {
454         if (hasEnteredFullscreen()) {
455             mContentsClientBridge.exitFullscreen(mWebContents);
456         }
457     }
458
459     @CalledByNative
460     public void onGetUrlFromManifest(String url) {
461         if (url != null && !url.isEmpty()) {
462             loadUrl(url, null);
463         }
464     }
465
466     @CalledByNative
467     public void onGetUrlAndLaunchScreenFromManifest(String url, String readyWhen, String imageBorder) {
468         if (url == null || url.isEmpty()) return;
469         mLaunchScreenManager.displayLaunchScreen(readyWhen, imageBorder);
470         mContentsClientBridge.registerPageLoadListener(mLaunchScreenManager);
471         loadUrl(url, null);
472     }
473
474     @CalledByNative
475     public void onGetFullscreenFlagFromManifest(boolean enterFullscreen) {
476         if (enterFullscreen) {
477             if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
478                 View decorView = mXWalkView.getActivity().getWindow().getDecorView();
479                 decorView.setSystemUiVisibility(
480                         View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
481                         View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
482                         View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
483                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
484                         View.SYSTEM_UI_FLAG_FULLSCREEN |
485                         View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
486             } else {
487                 mXWalkView.getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
488             }
489         }
490     }
491
492     public void destroy() {
493         if (mXWalkContent == 0) return;
494
495         XWalkPreferencesInternal.unload(this);
496         // Reset existing notification service in order to destruct it.
497         setNotificationService(null);
498         // Remove its children used for page rendering from view hierarchy.
499         removeView(mContentView);
500         removeView(mContentViewRenderView);
501         mContentViewRenderView.setCurrentContentViewCore(null);
502
503         // Destroy the native resources.
504         mContentViewRenderView.destroy();
505         mContentViewCore.destroy();
506
507         mCleanupReference.cleanupNow();
508         mCleanupReference = null;
509         mXWalkContent = 0;
510     }
511
512     public int getRoutingID() {
513         return nativeGetRoutingID(mXWalkContent);
514     }
515
516     //--------------------------------------------------------------------------------------------
517     private class XWalkIoThreadClientImpl implements XWalkContentsIoThreadClient {
518         // All methods are called on the IO thread.
519
520         @Override
521         public int getCacheMode() {
522             return mSettings.getCacheMode();
523         }
524
525         @Override
526         public InterceptedRequestData shouldInterceptRequest(final String url,
527                 boolean isMainFrame) {
528
529             // Notify a resource load is started. This is not the best place to start the callback
530             // but it's a workable way.
531             mContentsClientBridge.getCallbackHelper().postOnResourceLoadStarted(url);
532
533             WebResourceResponse webResourceResponse = mContentsClientBridge.shouldInterceptRequest(url);
534             InterceptedRequestData interceptedRequestData = null;
535
536             if (webResourceResponse == null) {
537                 mContentsClientBridge.getCallbackHelper().postOnLoadResource(url);
538             } else {
539                 if (isMainFrame && webResourceResponse.getData() == null) {
540                     mContentsClientBridge.getCallbackHelper().postOnReceivedError(
541                             XWalkResourceClientInternal.ERROR_UNKNOWN, null, url);
542                 }
543                 interceptedRequestData = new InterceptedRequestData(webResourceResponse.getMimeType(),
544                                                                     webResourceResponse.getEncoding(),
545                                                                     webResourceResponse.getData());
546             }
547             return interceptedRequestData;
548         }
549
550         @Override
551         public boolean shouldBlockContentUrls() {
552             return !mSettings.getAllowContentAccess();
553         }
554
555         @Override
556         public boolean shouldBlockFileUrls() {
557             return !mSettings.getAllowFileAccess();
558         }
559
560         @Override
561         public boolean shouldBlockNetworkLoads() {
562             return mSettings.getBlockNetworkLoads();
563         }
564
565         @Override
566         public void onDownloadStart(String url,
567                                     String userAgent,
568                                     String contentDisposition,
569                                     String mimeType,
570                                     long contentLength) {
571             mContentsClientBridge.getCallbackHelper().postOnDownloadStart(url, userAgent,
572                     contentDisposition, mimeType, contentLength);
573         }
574
575         @Override
576         public void newLoginRequest(String realm, String account, String args) {
577             mContentsClientBridge.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args);
578         }
579     }
580
581     private class XWalkGeolocationCallback implements XWalkGeolocationPermissions.Callback {
582         @Override
583         public void invoke(final String origin, final boolean allow, final boolean retain) {
584             ThreadUtils.runOnUiThread(new Runnable() {
585                 @Override
586                 public void run() {
587                     if (retain) {
588                         if (allow) {
589                             mGeolocationPermissions.allow(origin);
590                         } else {
591                             mGeolocationPermissions.deny(origin);
592                         }
593                     }
594                     nativeInvokeGeolocationCallback(mXWalkContent, allow, origin);
595                 }
596             });
597         }
598     }
599
600     @CalledByNative
601     private void onGeolocationPermissionsShowPrompt(String origin) {
602         // Reject if geolocation is disabled, or the origin has a retained deny.
603         if (!mSettings.getGeolocationEnabled()) {
604             nativeInvokeGeolocationCallback(mXWalkContent, false, origin);
605             return;
606         }
607         // Allow if the origin has a retained allow.
608         if (mGeolocationPermissions.hasOrigin(origin)) {
609             nativeInvokeGeolocationCallback(mXWalkContent,
610                     mGeolocationPermissions.isOriginAllowed(origin),
611                     origin);
612             return;
613         }
614         mContentsClientBridge.onGeolocationPermissionsShowPrompt(
615                 origin, new XWalkGeolocationCallback());
616     }
617
618     @CalledByNative
619     public void onGeolocationPermissionsHidePrompt() {
620         mContentsClientBridge.onGeolocationPermissionsHidePrompt();
621     }
622
623     public String enableRemoteDebugging(int allowedUid) {
624         // Chrome looks for "devtools_remote" pattern in the name of a unix domain socket
625         // to identify a debugging page
626         final String socketName = getContext().getApplicationContext().getPackageName() + "_devtools_remote";
627         if (mDevToolsServer == null) {
628             mDevToolsServer = new XWalkDevToolsServer(socketName);
629             mDevToolsServer.allowConnectionFromUid(allowedUid);
630             mDevToolsServer.setRemoteDebuggingEnabled(true);
631         }
632         // devtools/page is hardcoded in devtools_http_handler_impl.cc (kPageUrlPrefix)
633         return "ws://" + socketName + "/devtools/page/" + devToolsAgentId();
634     }
635
636     // Enables remote debugging and returns the URL at which the dev tools server is listening
637     // for commands. Only the current process is allowed to connect to the server.
638     String enableRemoteDebugging() {
639         return enableRemoteDebugging(getContext().getApplicationInfo().uid);
640     }
641
642     void disableRemoteDebugging() {
643         if (mDevToolsServer ==  null) return;
644
645         if (mDevToolsServer.isRemoteDebuggingEnabled()) {
646             mDevToolsServer.setRemoteDebuggingEnabled(false);
647         }
648         mDevToolsServer.destroy();
649         mDevToolsServer = null;
650     }
651
652     @Override
653     public void onKeyValueChanged(String key, XWalkPreferencesInternal.PreferenceValue value) {
654         if (key == null) return;
655         if (key.equals(XWalkPreferencesInternal.REMOTE_DEBUGGING)) {
656             if (value.getBooleanValue()) enableRemoteDebugging();
657             else disableRemoteDebugging();
658         } else if (key.equals(XWalkPreferencesInternal.ENABLE_JAVASCRIPT)) {
659             if (mSettings != null) {
660                 mSettings.setJavaScriptEnabled(value.getBooleanValue());
661             }
662         } else if (key.equals(XWalkPreferencesInternal.JAVASCRIPT_CAN_OPEN_WINDOW)) {
663             if (mSettings != null) {
664                 mSettings.setJavaScriptCanOpenWindowsAutomatically(value.getBooleanValue());
665             }
666         } else if (key.equals(XWalkPreferencesInternal.ALLOW_UNIVERSAL_ACCESS_FROM_FILE)) {
667             if (mSettings != null) {
668                 mSettings.setAllowUniversalAccessFromFileURLs(value.getBooleanValue());
669             }
670         } else if (key.equals(XWalkPreferencesInternal.SUPPORT_MULTIPLE_WINDOWS)) {
671             if (mSettings != null) {
672                 mSettings.setSupportMultipleWindows(value.getBooleanValue());
673             }
674         }
675     }
676
677     public void setOverlayVideoMode(boolean enabled) {
678         if (mContentViewRenderView != null) {
679             mContentViewRenderView.setOverlayVideoMode(enabled);
680         }
681     }
682
683     private native long nativeInit(XWalkWebContentsDelegate webViewContentsDelegate,
684             XWalkContentsClientBridge bridge);
685     private static native void nativeDestroy(long nativeXWalkContent);
686     private native long nativeGetWebContents(long nativeXWalkContent,
687             XWalkContentsIoThreadClient ioThreadClient,
688             InterceptNavigationDelegate delegate);
689     private native void nativeClearCache(long nativeXWalkContent, boolean includeDiskFiles);
690     private native String nativeDevToolsAgentId(long nativeXWalkContent);
691     private native String nativeGetVersion(long nativeXWalkContent);
692     private native void nativeSetJsOnlineProperty(long nativeXWalkContent, boolean networkUp);
693     private native boolean nativeSetManifest(long nativeXWalkContent, String path, String manifest);
694     private native int nativeGetRoutingID(long nativeXWalkContent);
695     private native void nativeInvokeGeolocationCallback(
696             long nativeXWalkContent, boolean value, String requestingFrame);
697     private native byte[] nativeGetState(long nativeXWalkContent);
698     private native boolean nativeSetState(long nativeXWalkContent, byte[] state);
699 }