b4fe3147d30d285d031616ff539b9809625f2696
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / XWalkContent.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013 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;
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.Bundle;
14 import android.text.TextUtils;
15 import android.util.AttributeSet;
16 import android.view.ViewGroup;
17 import android.webkit.WebResourceResponse;
18 import android.widget.FrameLayout;
19
20 import org.chromium.base.CalledByNative;
21 import org.chromium.base.JNINamespace;
22 import org.chromium.base.ThreadUtils;
23 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
24 import org.chromium.content.browser.ContentVideoView;
25 import org.chromium.content.browser.ContentView;
26 import org.chromium.content.browser.ContentViewCore;
27 import org.chromium.content.browser.ContentViewRenderView;
28 import org.chromium.content.browser.ContentViewStatics;
29 import org.chromium.content.browser.LoadUrlParams;
30 import org.chromium.content.browser.NavigationHistory;
31 import org.chromium.media.MediaPlayerBridge;
32 import org.chromium.ui.base.ActivityWindowAndroid;
33
34 @JNINamespace("xwalk")
35 /**
36  * This class is the implementation class for XWalkView by calling internal
37  * various classes.
38  */
39 public class XWalkContent extends FrameLayout {
40     private ContentViewCore mContentViewCore;
41     private ContentView mContentView;
42     private ContentViewRenderView mContentViewRenderView;
43     private ActivityWindowAndroid mWindow;
44     private XWalkView mXWalkView;
45     private XWalkContentsClientBridge mContentsClientBridge;
46     private XWalkContentsIoThreadClient mIoThreadClient;
47     private XWalkWebContentsDelegateAdapter mXWalkContentsDelegateAdapter;
48     private XWalkSettings mSettings;
49     private XWalkGeolocationPermissions mGeolocationPermissions;
50     private XWalkLaunchScreenManager mLaunchScreenManager;
51
52     int mXWalkContent;
53     int mWebContents;
54     boolean mReadyToLoad = false;
55     String mPendingUrl = null;
56
57     public XWalkContent(Context context, AttributeSet attrs, XWalkView xwView) {
58         super(context, attrs);
59
60         // Initialize the WebContensDelegate.
61         mXWalkView = xwView;
62         mContentsClientBridge = new XWalkContentsClientBridge(mXWalkView);
63         mXWalkContentsDelegateAdapter = new XWalkWebContentsDelegateAdapter(
64             mContentsClientBridge);
65         mIoThreadClient = new XWalkIoThreadClientImpl();
66
67         // Initialize mWindow which is needed by content
68         mWindow = new ActivityWindowAndroid(xwView.getActivity());
69
70         // Initialize ContentViewRenderView
71         mContentViewRenderView = new ContentViewRenderView(context, mWindow) {
72             protected void onReadyToRender() {
73                 if (mPendingUrl != null) {
74                     doLoadUrl(mPendingUrl);
75                     mPendingUrl = null;
76                 }
77
78                 mReadyToLoad = true;
79             }
80         };
81         mLaunchScreenManager = new XWalkLaunchScreenManager(context, mXWalkView);
82         mContentViewRenderView.registerFirstRenderedFrameListener(mLaunchScreenManager);
83         addView(mContentViewRenderView,
84                 new FrameLayout.LayoutParams(
85                         FrameLayout.LayoutParams.MATCH_PARENT,
86                         FrameLayout.LayoutParams.MATCH_PARENT));
87
88         mXWalkContent = nativeInit(mXWalkContentsDelegateAdapter, mContentsClientBridge);
89         mWebContents = nativeGetWebContents(mXWalkContent, mIoThreadClient,
90                 mContentsClientBridge.getInterceptNavigationDelegate());
91
92         // Initialize ContentView.
93         mContentView = ContentView.newInstance(getContext(), mWebContents, mWindow);
94         addView(mContentView,
95                 new FrameLayout.LayoutParams(
96                         FrameLayout.LayoutParams.MATCH_PARENT,
97                         FrameLayout.LayoutParams.MATCH_PARENT));
98         mContentView.setContentViewClient(mContentsClientBridge);
99
100         mContentViewRenderView.setCurrentContentView(mContentView);
101
102         // For addJavascriptInterface
103         mContentViewCore = mContentView.getContentViewCore();
104         mContentsClientBridge.installWebContentsObserver(mContentViewCore);
105
106         mContentView.getContentViewCore().setDownloadDelegate(mContentsClientBridge);
107
108         // Set the third argument isAccessFromFileURLsGrantedByDefault to false, so that
109         // the members mAllowUniversalAccessFromFileURLs and mAllowFileAccessFromFileURLs
110         // won't be changed from false to true at the same time in the constructor of
111         // XWalkSettings class.
112         mSettings = new XWalkSettings(getContext(), mWebContents, false);
113         // Enable AllowFileAccessFromFileURLs, so that files under file:// path could be
114         // loaded by XMLHttpRequest.
115         mSettings.setAllowFileAccessFromFileURLs(true);
116
117         SharedPreferences sharedPreferences = new InMemorySharedPreferences();
118         mGeolocationPermissions = new XWalkGeolocationPermissions(sharedPreferences);
119
120         MediaPlayerBridge.setResourceLoadingFilter(
121                 new XWalkMediaPlayerResourceLoadingFilter());
122     }
123
124     void doLoadUrl(String url) {
125         //TODO(Xingnan): Configure appropriate parameters here.
126         // Handle the same url loading by parameters.
127         if (TextUtils.equals(url, mContentView.getUrl())) {
128             mContentView.getContentViewCore().reload(true);
129         } else {
130             LoadUrlParams params = new LoadUrlParams(url);
131             params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE);
132             mContentView.loadUrl(params);
133         }
134
135         mContentView.requestFocus();
136     }
137
138     public void loadUrl(String url) {
139         if (url == null)
140             return;
141
142         if (mReadyToLoad)
143             doLoadUrl(url);
144         else
145             mPendingUrl = url;
146     }
147
148     public void reload() {
149         if (mReadyToLoad) {
150             mContentView.getContentViewCore().reload(true);
151         }
152     }
153
154     public String getUrl() {
155         String url = mContentView.getUrl();
156         if (url == null || url.trim().isEmpty()) return null;
157         return url;
158     }
159
160     public String getTitle() {
161         String title = mContentView.getTitle().trim();
162         if (title == null) title = "";
163         return title;
164     }
165
166     public void addJavascriptInterface(Object object, String name) {
167         mContentViewCore.addJavascriptInterface(object, name);
168     }
169
170     public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
171         mContentsClientBridge.setXWalkWebChromeClient(client);
172     }
173
174     public XWalkWebChromeClient getXWalkWebChromeClient() {
175         return mContentsClientBridge.getXWalkWebChromeClient();
176     }
177
178     public void setXWalkClient(XWalkClient client) {
179         mContentsClientBridge.setXWalkClient(client);
180     }
181
182     public void setDownloadListener(DownloadListener listener) {
183         mContentsClientBridge.setDownloadListener(listener);
184     }
185
186     public void setNavigationHandler(XWalkNavigationHandler handler) {
187         mContentsClientBridge.setNavigationHandler(handler);
188     }
189
190     public void setNotificationService(XWalkNotificationService service) {
191         mContentsClientBridge.setNotificationService(service);
192     }
193
194     public void onPause() {
195         mContentView.onHide();
196     }
197
198     public void onResume() {
199         mContentView.onShow();
200     }
201
202     public void onActivityResult(int requestCode, int resultCode, Intent data) {
203         mWindow.onActivityResult(requestCode, resultCode, data);
204     }
205
206     public boolean onNewIntent(Intent intent) {
207         return mContentsClientBridge.onNewIntent(intent);
208     }
209
210     public void clearCache(boolean includeDiskFiles) {
211         if (mXWalkContent == 0) return;
212         nativeClearCache(mXWalkContent, includeDiskFiles);
213     }
214
215     public void clearHistory() {
216         mContentView.getContentViewCore().clearHistory();
217     }
218
219     public boolean canGoBack() {
220         return mContentView.canGoBack();
221     }
222
223     public void goBack() {
224         mContentView.goBack();
225     }
226
227     public boolean canGoForward() {
228         return mContentView.canGoForward();
229     }
230
231     public void goForward() {
232         mContentView.goForward();
233     }
234
235     public void stopLoading() {
236         mContentView.getContentViewCore().stopLoading();
237     }
238
239     // TODO(Guangzhen): ContentViewStatics will be removed in upstream,
240     // details in content_view_statics.cc.
241     // We need follow up after upstream updates that.
242     public void pauseTimers() {
243         ContentViewStatics.setWebKitSharedTimersSuspended(true);
244     }
245
246     public void resumeTimers() {
247         ContentViewStatics.setWebKitSharedTimersSuspended(false);
248     }
249
250     public String getOriginalUrl() {
251         NavigationHistory history = mContentViewCore.getNavigationHistory();
252         int currentIndex = history.getCurrentEntryIndex();
253         if (currentIndex >= 0 && currentIndex < history.getEntryCount()) {
254             return history.getEntryAtIndex(currentIndex).getOriginalUrl();
255         }
256         return null;
257     }
258
259     public String getVersion() {
260         if (mXWalkContent == 0) return "";
261         return nativeGetVersion(mXWalkContent);
262     }
263
264     public void setNetworkAvailable(boolean networkUp) {
265         if (mXWalkContent == 0) return;
266         nativeSetJsOnlineProperty(mXWalkContent, networkUp);
267     }
268
269     // For instrumentation test.
270     public ContentViewCore getContentViewCoreForTest() {
271         return mContentViewCore;
272     }
273
274     // For instrumentation test.
275     public void installWebContentsObserverForTest(XWalkContentsClient contentClient) {
276         contentClient.installWebContentsObserver(mContentViewCore);
277     }
278
279     public String devToolsAgentId() {
280         if (mXWalkContent == 0) return "";
281         return nativeDevToolsAgentId(mXWalkContent);
282     }
283
284     public XWalkSettings getSettings() {
285         return mSettings;
286     }
287
288     public void loadAppFromManifest(String path, String manifest) {
289         if (path == null || manifest == null || mXWalkContent == 0) {
290             return;
291         }
292
293         if (!nativeSetManifest(mXWalkContent, path, manifest)) {
294             throw new RuntimeException("Failed to parse the manifest file.");
295         }
296     }
297
298     public WebBackForwardList copyBackForwardList() {
299         return new WebBackForwardList(mContentViewCore.getNavigationHistory());
300     }
301
302     public static final String SAVE_RESTORE_STATE_KEY = "XWALKVIEW_STATE";
303
304     public WebBackForwardList saveState(Bundle outState) {
305         if (outState == null) return null;
306
307         byte[] state = nativeGetState(mXWalkContent);
308         if (state == null) return null;
309
310         outState.putByteArray(SAVE_RESTORE_STATE_KEY, state);
311         return copyBackForwardList();
312     }
313
314     public WebBackForwardList restoreState(Bundle inState) {
315         if (inState == null) return null;
316
317         byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY);
318         if (state == null) return null;
319
320         boolean result = nativeSetState(mXWalkContent, state);
321
322         // The onUpdateTitle callback normally happens when a page is loaded,
323         // but is optimized out in the restoreState case because the title is
324         // already restored. See WebContentsImpl::UpdateTitleForEntry. So we
325         // call the callback explicitly here.
326         if (result) {
327             mContentsClientBridge.onUpdateTitle(mContentViewCore.getTitle());
328         }
329
330         return result ? copyBackForwardList() : null;
331     }
332
333     public boolean isFullscreen() {
334         return mWebContents != 0 && getXWalkWebChromeClient() != null &&
335                 getXWalkWebChromeClient().isFullscreen();
336     }
337
338     public void exitFullscreen() {
339         if (isFullscreen()) {
340             mContentsClientBridge.exitFullscreen(mWebContents);
341         }
342     }
343
344     @CalledByNative
345     public void onGetUrlFromManifest(String url) {
346         if (url != null && !url.isEmpty()) {
347             loadUrl(url);
348         }
349     }
350
351     @CalledByNative
352     public void onGetUrlAndLaunchScreenFromManifest(String url, String readyWhen) {
353         if (url == null || url.isEmpty()) return;
354         mLaunchScreenManager.displayLaunchScreen(readyWhen);
355         mContentsClientBridge.registerPageLoadListener(mLaunchScreenManager);
356         loadUrl(url);
357     }
358
359     public void destroy() {
360         if (mXWalkContent == 0) return;
361
362         // Reset existing notification service in order to destruct it.
363         setNotificationService(null);
364         // Remove its children used for page rendering from view hierarchy.
365         removeView(mContentView);
366         removeView(mContentViewRenderView);
367         mContentViewRenderView.setCurrentContentView(null);
368
369         // Destroy the native resources.
370         mContentViewRenderView.destroy();
371         mContentView.destroy();
372
373         nativeDestroy(mXWalkContent);
374         mXWalkContent = 0;
375     }
376
377     public int getRoutingID() {
378         return nativeGetRoutingID(mXWalkContent);
379     }
380
381     //--------------------------------------------------------------------------------------------
382     private class XWalkIoThreadClientImpl implements XWalkContentsIoThreadClient {
383         // All methods are called on the IO thread.
384
385         @Override
386         public int getCacheMode() {
387             return mSettings.getCacheMode();
388         }
389
390         @Override
391         public InterceptedRequestData shouldInterceptRequest(final String url,
392                 boolean isMainFrame) {
393
394             WebResourceResponse webResourceResponse = mContentsClientBridge.shouldInterceptRequest(url);
395             InterceptedRequestData interceptedRequestData = null;
396
397             if (webResourceResponse == null) {
398                 mContentsClientBridge.getCallbackHelper().postOnLoadResource(url);
399             } else {
400                 if (isMainFrame && webResourceResponse.getData() == null) {
401                     mContentsClientBridge.getCallbackHelper().postOnReceivedError(-1, null, url);
402                 }
403                 interceptedRequestData = new InterceptedRequestData(webResourceResponse.getMimeType(),
404                                                                     webResourceResponse.getEncoding(),
405                                                                     webResourceResponse.getData());
406             }
407             return interceptedRequestData;
408         }
409
410         @Override
411         public boolean shouldBlockContentUrls() {
412             return !mSettings.getAllowContentAccess();
413         }
414
415         @Override
416         public boolean shouldBlockFileUrls() {
417             return !mSettings.getAllowFileAccess();
418         }
419
420         @Override
421         public boolean shouldBlockNetworkLoads() {
422             return mSettings.getBlockNetworkLoads();
423         }
424
425         @Override
426         public void onDownloadStart(String url,
427                                     String userAgent,
428                                     String contentDisposition,
429                                     String mimeType,
430                                     long contentLength) {
431             mContentsClientBridge.getCallbackHelper().postOnDownloadStart(url, userAgent,
432                     contentDisposition, mimeType, contentLength);
433         }
434
435         @Override
436         public void newLoginRequest(String realm, String account, String args) {
437             mContentsClientBridge.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args);
438         }
439     }
440
441     private class XWalkGeolocationCallback implements XWalkGeolocationPermissions.Callback {
442         @Override
443         public void invoke(final String origin, final boolean allow, final boolean retain) {
444             ThreadUtils.runOnUiThread(new Runnable() {
445                 @Override
446                 public void run() {
447                     if (retain) {
448                         if (allow) {
449                             mGeolocationPermissions.allow(origin);
450                         } else {
451                             mGeolocationPermissions.deny(origin);
452                         }
453                     }
454                     nativeInvokeGeolocationCallback(mXWalkContent, allow, origin);
455                 }
456             });
457         }
458     }
459
460     @CalledByNative
461     private void onGeolocationPermissionsShowPrompt(String origin) {
462         // Reject if geolocation is disabled, or the origin has a retained deny.
463         if (!mSettings.getGeolocationEnabled()) {
464             nativeInvokeGeolocationCallback(mXWalkContent, false, origin);
465             return;
466         }
467         // Allow if the origin has a retained allow.
468         if (mGeolocationPermissions.hasOrigin(origin)) {
469             nativeInvokeGeolocationCallback(mXWalkContent,
470                     mGeolocationPermissions.isOriginAllowed(origin),
471                     origin);
472             return;
473         }
474         mContentsClientBridge.onGeolocationPermissionsShowPrompt(
475                 origin, new XWalkGeolocationCallback());
476     }
477
478     @CalledByNative
479     public void onGeolocationPermissionsHidePrompt() {
480         mContentsClientBridge.onGeolocationPermissionsHidePrompt();
481     }
482
483     private native int nativeInit(XWalkWebContentsDelegate webViewContentsDelegate,
484             XWalkContentsClientBridge bridge);
485     private static native void nativeDestroy(int nativeXWalkContent);
486     private native int nativeGetWebContents(int nativeXWalkContent,
487             XWalkContentsIoThreadClient ioThreadClient,
488             InterceptNavigationDelegate delegate);
489     private native void nativeClearCache(int nativeXWalkContent, boolean includeDiskFiles);
490     private native String nativeDevToolsAgentId(int nativeXWalkContent);
491     private native String nativeGetVersion(int nativeXWalkContent);
492     private native void nativeSetJsOnlineProperty(int nativeXWalkContent, boolean networkUp);
493     private native boolean nativeSetManifest(int nativeXWalkContent, String path, String manifest);
494     private native int nativeGetRoutingID(int nativeXWalkContent);
495     private native void nativeInvokeGeolocationCallback(
496             int nativeXWalkContent, boolean value, String requestingFrame);
497     private native byte[] nativeGetState(int nativeXWalkContent);
498     private native boolean nativeSetState(int nativeXWalkContent, byte[] state);
499 }