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