Upstream version 6.34.113.0
[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     @CalledByNative
360     public void onGetFullscreenFlagFromManifest(boolean enterFullscreen) {
361         if (enterFullscreen) getXWalkWebChromeClient().onToggleFullscreen(true);
362     }
363
364     public void destroy() {
365         if (mXWalkContent == 0) return;
366
367         // Reset existing notification service in order to destruct it.
368         setNotificationService(null);
369         // Remove its children used for page rendering from view hierarchy.
370         removeView(mContentView);
371         removeView(mContentViewRenderView);
372         mContentViewRenderView.setCurrentContentView(null);
373
374         // Destroy the native resources.
375         mContentViewRenderView.destroy();
376         mContentView.destroy();
377
378         nativeDestroy(mXWalkContent);
379         mXWalkContent = 0;
380     }
381
382     public int getRoutingID() {
383         return nativeGetRoutingID(mXWalkContent);
384     }
385
386     //--------------------------------------------------------------------------------------------
387     private class XWalkIoThreadClientImpl implements XWalkContentsIoThreadClient {
388         // All methods are called on the IO thread.
389
390         @Override
391         public int getCacheMode() {
392             return mSettings.getCacheMode();
393         }
394
395         @Override
396         public InterceptedRequestData shouldInterceptRequest(final String url,
397                 boolean isMainFrame) {
398
399             WebResourceResponse webResourceResponse = mContentsClientBridge.shouldInterceptRequest(url);
400             InterceptedRequestData interceptedRequestData = null;
401
402             if (webResourceResponse == null) {
403                 mContentsClientBridge.getCallbackHelper().postOnLoadResource(url);
404             } else {
405                 if (isMainFrame && webResourceResponse.getData() == null) {
406                     mContentsClientBridge.getCallbackHelper().postOnReceivedError(-1, null, url);
407                 }
408                 interceptedRequestData = new InterceptedRequestData(webResourceResponse.getMimeType(),
409                                                                     webResourceResponse.getEncoding(),
410                                                                     webResourceResponse.getData());
411             }
412             return interceptedRequestData;
413         }
414
415         @Override
416         public boolean shouldBlockContentUrls() {
417             return !mSettings.getAllowContentAccess();
418         }
419
420         @Override
421         public boolean shouldBlockFileUrls() {
422             return !mSettings.getAllowFileAccess();
423         }
424
425         @Override
426         public boolean shouldBlockNetworkLoads() {
427             return mSettings.getBlockNetworkLoads();
428         }
429
430         @Override
431         public void onDownloadStart(String url,
432                                     String userAgent,
433                                     String contentDisposition,
434                                     String mimeType,
435                                     long contentLength) {
436             mContentsClientBridge.getCallbackHelper().postOnDownloadStart(url, userAgent,
437                     contentDisposition, mimeType, contentLength);
438         }
439
440         @Override
441         public void newLoginRequest(String realm, String account, String args) {
442             mContentsClientBridge.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args);
443         }
444     }
445
446     private class XWalkGeolocationCallback implements XWalkGeolocationPermissions.Callback {
447         @Override
448         public void invoke(final String origin, final boolean allow, final boolean retain) {
449             ThreadUtils.runOnUiThread(new Runnable() {
450                 @Override
451                 public void run() {
452                     if (retain) {
453                         if (allow) {
454                             mGeolocationPermissions.allow(origin);
455                         } else {
456                             mGeolocationPermissions.deny(origin);
457                         }
458                     }
459                     nativeInvokeGeolocationCallback(mXWalkContent, allow, origin);
460                 }
461             });
462         }
463     }
464
465     @CalledByNative
466     private void onGeolocationPermissionsShowPrompt(String origin) {
467         // Reject if geolocation is disabled, or the origin has a retained deny.
468         if (!mSettings.getGeolocationEnabled()) {
469             nativeInvokeGeolocationCallback(mXWalkContent, false, origin);
470             return;
471         }
472         // Allow if the origin has a retained allow.
473         if (mGeolocationPermissions.hasOrigin(origin)) {
474             nativeInvokeGeolocationCallback(mXWalkContent,
475                     mGeolocationPermissions.isOriginAllowed(origin),
476                     origin);
477             return;
478         }
479         mContentsClientBridge.onGeolocationPermissionsShowPrompt(
480                 origin, new XWalkGeolocationCallback());
481     }
482
483     @CalledByNative
484     public void onGeolocationPermissionsHidePrompt() {
485         mContentsClientBridge.onGeolocationPermissionsHidePrompt();
486     }
487
488     private native int nativeInit(XWalkWebContentsDelegate webViewContentsDelegate,
489             XWalkContentsClientBridge bridge);
490     private static native void nativeDestroy(int nativeXWalkContent);
491     private native int nativeGetWebContents(int nativeXWalkContent,
492             XWalkContentsIoThreadClient ioThreadClient,
493             InterceptNavigationDelegate delegate);
494     private native void nativeClearCache(int nativeXWalkContent, boolean includeDiskFiles);
495     private native String nativeDevToolsAgentId(int nativeXWalkContent);
496     private native String nativeGetVersion(int nativeXWalkContent);
497     private native void nativeSetJsOnlineProperty(int nativeXWalkContent, boolean networkUp);
498     private native boolean nativeSetManifest(int nativeXWalkContent, String path, String manifest);
499     private native int nativeGetRoutingID(int nativeXWalkContent);
500     private native void nativeInvokeGeolocationCallback(
501             int nativeXWalkContent, boolean value, String requestingFrame);
502     private native byte[] nativeGetState(int nativeXWalkContent);
503     private native boolean nativeSetState(int nativeXWalkContent, byte[] state);
504 }