Upstream version 7.35.143.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / XWalkView.java
1 // Copyright (c) 2013-2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.core;
6
7 import android.app.Activity;
8 import android.app.AlertDialog;
9 import android.app.ApplicationErrorReport;
10 import android.content.Context;
11 import android.content.DialogInterface;
12 import android.content.Intent;
13 import android.graphics.Rect;
14 import android.net.Uri;
15 import android.os.Bundle;
16 import android.os.Looper;
17 import android.util.AttributeSet;
18 import android.view.KeyEvent;
19 import android.view.ViewGroup;
20 import android.webkit.ValueCallback;
21 import android.widget.FrameLayout;
22
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25
26 import org.chromium.base.ActivityState;
27 import org.chromium.base.ApplicationStatus;
28
29 import org.xwalk.core.extension.XWalkExtensionManager;
30
31 /**
32  * <p>XWalkView represents an Android view for web apps/pages. Thus most of attributes
33  * for Android view are valid for this class. Since it internally uses
34  * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
35  * android.view.SurfaceView</a> for rendering web pages by default, it can't be resized,
36  * rotated, transformed and animated due to the limitations of SurfaceView.
37  * Alternatively, if the preference key {@link XWalkPreferences#ANIMATABLE_XWALK_VIEW}
38  * is set to True, XWalkView can be transformed and animated because
39  * <a href="http://developer.android.com/reference/android/view/TextureView.html">
40  * TextureView</a> is intentionally used to render web pages for animation support.
41  * Besides, XWalkView won't be rendered if it's invisible.</p>
42  *
43  * <p>XWalkView needs hardware acceleration to render web pages. As a result, the
44  * AndroidManifest.xml of the caller's app must be appended with the attribute
45  * "android:hardwareAccelerated" and its value must be set as "true".</p>
46  * <pre>
47  * &lt;application android:name="android.app.Application" android:label="XWalkUsers"
48  *     android:hardwareAccelerated="true"&gt;
49  * </pre>
50  *
51  * <p>Crosswalk provides 2 major callback classes, namely {@link XWalkResourceClient} and
52  * {@link XWalkUIClient} for listening to the events related to resource loading and UI.
53  * By default, Crosswalk has a default implementation. Callers can override them if needed.</p>
54  *
55  * <p>Unlike other Android views, this class has to listen to system events like application life
56  * cycle, intents, and activity result. The web engine inside this view need to get and handle
57  * them. For example:</p>
58  *
59  * <pre>
60  *   import android.app.Activity;
61  *   import android.os.Bundle;
62  *
63  *   import org.xwalk.core.XWalkResourceClient;
64  *   import org.xwalk.core.XWalkUIClient;
65  *   import org.xwalk.core.XWalkView;
66  *
67  *   public class MyActivity extends Activity {
68  *       XWalkView mXwalkView;
69  *
70  *       class MyResourceClient extends XWalkResourceClient {
71  *           MyResourceClient(XWalkView view) {
72  *               super(view);
73  *           }
74  *
75  *           &#64;Override
76  *           WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) {
77  *               // Handle it here.
78  *               ...
79  *           }
80  *       }
81  *
82  *       class MyUIClient extends XWalkUIClient {
83  *           MyUIClient(XWalkView view) {
84  *               super(view);
85  *           }
86  *
87  *           &#64;Override
88  *           void onFullscreenToggled(XWalkView view, String url) {
89  *               // Handle it here.
90  *               ...
91  *           }
92  *       }
93  *
94  *       &#64;Override
95  *       protected void onCreate(Bundle savedInstanceState) {
96  *           mXwalkView = new XWalkView(this, null);
97  *           setContentView(mXwalkView);
98  *           mXwalkView.setResourceClient(new MyResourceClient(mXwalkView));
99  *           mXwalkView.setUIClient(new MyUIClient(mXwalkView));
100  *           mXwalkView.load("http://www.crosswalk-project.org", null);
101  *       }
102  *
103  *       &#64;Override
104  *       protected void onPause() {
105  *           super.onPause();
106  *           if (mXwalkView != null) {
107  *               mXwalkView.pauseTimers();
108  *               mXwalkView.onHide();
109  *           }
110  *       }
111  *
112  *       &#64;Override
113  *       protected void onResume() {
114  *           super.onResume();
115  *           if (mXwalkView != null) {
116  *               mXwalkView.resumeTimers();
117  *               mXwalkView.onShow();
118  *           }
119  *       }
120  *
121  *       &#64;Override
122  *       protected void onDestroy() {
123  *           super.onDestroy();
124  *           if (mXwalkView != null) {
125  *               mXwalkView.onDestroy();
126  *           }
127  *       }
128  *
129  *       &#64;Override
130  *       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
131  *           if (mXwalkView != null) {
132  *               mXwalkView.onActivityResult(requestCode, resultCode, data);
133  *           }
134  *       }
135  *
136  *       &#64;Override
137  *       protected void onNewIntent(Intent intent) {
138  *           if (mXwalkView != null) {
139  *               mXwalkView.onNewIntent(intent);
140  *           }
141  *       }
142  *   }
143  * </pre>
144  */
145 public class XWalkView extends android.widget.FrameLayout {
146
147     static final String PLAYSTORE_DETAIL_URI = "market://details?id=";
148
149     private XWalkContent mContent;
150     private Activity mActivity;
151     private Context mContext;
152     private XWalkExtensionManager mExtensionManager;
153     private boolean mIsHidden;
154
155     /** Normal reload mode as default. */
156     public static final int RELOAD_NORMAL = 0;
157     /** Reload mode with bypassing the cache. */
158     public static final int RELOAD_IGNORE_CACHE = 1;
159
160     /**
161      * Constructor for inflating via XML.
162      * @param context  a Context object used to access application assets.
163      * @param attrs    an AttributeSet passed to our parent.
164      */
165     public XWalkView(Context context, AttributeSet attrs) {
166         super(context, attrs);
167
168         checkThreadSafety();
169         mContext = context;
170         init(context, attrs);
171     }
172
173     /**
174      * Constructor for Crosswalk runtime. In shared mode, context isi
175      * different from activity. In embedded mode, they're same.
176      * @param context  a Context object used to access application assets
177      * @param activity the activity for this XWalkView.
178      */
179     public XWalkView(Context context, Activity activity) {
180         super(context, null);
181         checkThreadSafety();
182
183         // Make sure mActivity is initialized before calling 'init' method.
184         mActivity = activity;
185         mContext = context;
186         init(context, null);
187     }
188
189     /**
190      * Get the current activity passed from callers. It's never null.
191      * @return the activity instance passed from callers.
192      *
193      * @hide
194      */
195     public Activity getActivity() {
196         if (mActivity != null) {
197             return mActivity;
198         } else if (getContext() instanceof Activity) {
199             return (Activity)getContext();
200         }
201
202         // Never achieve here.
203         assert(false);
204         return null;
205     }
206
207     // TODO(yongsheng): we should remove this since we have getContext()?
208     /**
209      * @hide
210      */
211     public Context getViewContext() {
212         return mContext;
213     }
214
215     private void init(Context context, AttributeSet attrs) {
216         // Initialize chromium resources. Assign them the correct ids in
217         // xwalk core.
218         XWalkInternalResources.resetIds(context);
219
220         // Intialize library, paks and others.
221         try {
222             XWalkViewDelegate.init(this);
223         } catch (UnsatisfiedLinkError e) {
224             final UnsatisfiedLinkError err = e;
225             final Activity activity = getActivity();
226             final String packageName = context.getPackageName();
227             String missingArch = XWalkViewDelegate.isRunningOnIA() ? "Intel" : "ARM";
228             final String message =
229                     context.getString(R.string.cpu_arch_mismatch_message, missingArch);
230
231             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
232             builder.setTitle(R.string.cpu_arch_mismatch_title)
233                     .setMessage(message)
234                     .setOnCancelListener(new DialogInterface.OnCancelListener() {
235                         @Override
236                         public void onCancel(DialogInterface dialog) {
237                             activity.finish();
238                         }
239                     }).setPositiveButton(R.string.goto_store_button_label,
240                             new DialogInterface.OnClickListener() {
241                         @Override
242                         public void onClick(DialogInterface dialog, int which) {
243                             activity.startActivity(new Intent(Intent.ACTION_VIEW,
244                                     Uri.parse(PLAYSTORE_DETAIL_URI + packageName)));
245                             activity.finish();
246                         }
247                     }).setNeutralButton(R.string.report_feedback_button_label,
248                             new DialogInterface.OnClickListener() {
249                         @Override
250                         public void onClick(DialogInterface dialog, int which) {
251                             ApplicationErrorReport report = new ApplicationErrorReport();
252                             report.type = ApplicationErrorReport.TYPE_CRASH;
253                             report.packageName = report.processName = packageName;
254
255                             ApplicationErrorReport.CrashInfo crash =
256                                     new ApplicationErrorReport.CrashInfo();
257                             crash.exceptionClassName = err.getClass().getSimpleName();
258                             crash.exceptionMessage = "CPU architecture mismatch";
259                             StringWriter writer = new StringWriter();
260                             PrintWriter print = new PrintWriter(writer);
261                             err.printStackTrace(print);
262                             crash.stackTrace = writer.toString();
263                             StackTraceElement stack = err.getStackTrace()[0];
264                             crash.throwClassName = stack.getClassName();
265                             crash.throwFileName = stack.getFileName();
266                             crash.throwLineNumber = stack.getLineNumber();
267                             crash.throwMethodName = stack.getMethodName();
268
269                             report.crashInfo = crash;
270                             report.systemApp = false;
271                             report.time = System.currentTimeMillis();
272
273                             Intent intent = new Intent(Intent.ACTION_APP_ERROR);
274                             intent.putExtra(Intent.EXTRA_BUG_REPORT, report);
275                             activity.startActivity(intent);
276                             activity.finish();
277                         }
278                     });
279             builder.create().show();
280             return;
281         }
282
283         initXWalkContent(context, attrs);
284     }
285
286     private void initXWalkContent(Context context, AttributeSet attrs) {
287         mIsHidden = false;
288         mContent = new XWalkContent(context, attrs, this);
289         addView(mContent,
290                 new FrameLayout.LayoutParams(
291                         FrameLayout.LayoutParams.MATCH_PARENT,
292                         FrameLayout.LayoutParams.MATCH_PARENT));
293
294
295         // Set default XWalkClientImpl.
296         setXWalkClient(new XWalkClient(this));
297         // Set default XWalkWebChromeClient and DownloadListener. The default actions
298         // are provided via the following clients if special actions are not needed.
299         setXWalkWebChromeClient(new XWalkWebChromeClient(this));
300
301         // Set with internal implementation. Could be overwritten by embedders'
302         // setting.
303         setUIClient(new XWalkUIClient(this));
304         setResourceClient(new XWalkResourceClient(this));
305
306         setDownloadListener(new XWalkDownloadListenerImpl(context));
307         setNavigationHandler(new XWalkNavigationHandlerImpl(context));
308         setNotificationService(new XWalkNotificationServiceImpl(context, this));
309
310         // Enable xwalk extension mechanism and start load extensions here.
311         // Note that it has to be after above initialization.
312         mExtensionManager = new XWalkExtensionManager(context, getActivity());
313         mExtensionManager.loadExtensions();
314     }
315
316     /**
317      * Load a web page/app from a given base URL or a content.
318      * If url is null or empty and content is null or empty, then this function
319      * will do nothing.
320      * If content is not null, load the web page/app from the content.
321      * If content is not null and the url is not set, return "about:blank" ifi
322      * calling {@link XWalkView#getUrl()}.
323      * If content is null, try to load the content from the url.
324      *
325      * It supports URL schemes like 'http:', 'https:' and 'file:'.
326      * It can also load files from Android assets, e.g. 'file:///android_asset/'.
327      * @param url the url for web page/app.
328      * @param content the content for the web page/app. Could be empty.
329      */
330     public void load(String url, String content) {
331         if (mContent == null) return;
332         checkThreadSafety();
333         mContent.loadUrl(url, content);
334     }
335
336     /**
337      * Load a web app from a given manifest.json file. If content is not null,
338      * load the manifest.json from the content. If content is null, try to load
339      * the manifest.json from the url. Note that url should not be null if the
340      * launched path defined in manifest.json is relative.
341      *
342      * It supports URL schemes like 'http:', 'https:' and 'file:'.
343      * It can also load files from Android assets, e.g. 'file:///android_asset/'.
344      * @param url the url for manifest.json.
345      * @param content the content for manifest.json.
346      */
347     public void loadAppFromManifest(String url, String content) {
348         if (mContent == null) return;
349         checkThreadSafety();
350         mContent.loadAppFromManifest(url, content);
351     }
352
353     /**
354      * Reload a web app with a given mode.
355      * @param mode the reload mode.
356      */
357     public void reload(int mode) {
358         if (mContent == null) return;
359         checkThreadSafety();
360         mContent.reload(mode);
361     }
362
363     /**
364      * Stop current loading progress.
365      */
366     public void stopLoading() {
367         if (mContent == null) return;
368         checkThreadSafety();
369         mContent.stopLoading();
370     }
371
372     /**
373      * Get the url of current web page/app. This may be different from what's passed
374      * by caller.
375      * @return the url for current web page/app.
376      */
377     public String getUrl() {
378         if (mContent == null) return null;
379         checkThreadSafety();
380         return mContent.getUrl();
381     }
382
383     /**
384      * Get the title of current web page/app. This may be different from what's passed
385      * by caller.
386      * @return the title for current web page/app.
387      */
388     public String getTitle() {
389         if (mContent == null) return null;
390         checkThreadSafety();
391         return mContent.getTitle();
392     }
393
394     /**
395      * Get the original url specified by caller.
396      * @return the original url.
397      */
398     public String getOriginalUrl() {
399         if (mContent == null) return null;
400         checkThreadSafety();
401         return mContent.getOriginalUrl();
402     }
403
404     /**
405      * Get the navigation history for current XWalkView. It's synchronized with
406      * this XWalkView if any backward/forward and navigation operations.
407      * @return the navigation history.
408      */
409     public XWalkNavigationHistory getNavigationHistory() {
410         if (mContent == null) return null;
411         checkThreadSafety();
412         return mContent.getNavigationHistory();
413     }
414
415     /**
416      * Injects the supplied Java object into this XWalkView.
417      * Each method defined in the class of the object should be
418      * marked with {@link JavascriptInterface} if it's called by JavaScript.
419      * @param object the supplied Java object, called by JavaScript.
420      * @param name the name injected in JavaScript.
421      */
422     public void addJavascriptInterface(Object object, String name) {
423         if (mContent == null) return;
424         checkThreadSafety();
425         mContent.addJavascriptInterface(object, name);
426     }
427
428     /**
429      * Evaluate a fragment of JavaScript code and get the result via callback.
430      * @param script the JavaScript string.
431      * @param callback the callback to handle the evaluated result.
432      */
433     public void evaluateJavascript(String script, ValueCallback<String> callback) {
434         if (mContent == null) return;
435         checkThreadSafety();
436         mContent.evaluateJavascript(script, callback);
437     }
438
439     /**
440      * Clear the resource cache. Note that the cache is per-application, so this
441      * will clear the cache for all XWalkViews used.
442      * @param includeDiskFiles indicate whether to clear disk files for cache.
443      */
444     public void clearCache(boolean includeDiskFiles) {
445         if (mContent == null) return;
446         checkThreadSafety();
447         mContent.clearCache(includeDiskFiles);
448     }
449
450     /**
451      * Indicate that a HTML element is occupying the whole screen.
452      * @return true if any HTML element is occupying the whole screen.
453      */
454     public boolean hasEnteredFullscreen() {
455         if (mContent == null) return false;
456         checkThreadSafety();
457         return mContent.hasEnteredFullscreen();
458     }
459
460     /**
461      * Leave fullscreen mode if it's. Do nothing if it's not
462      * in fullscreen.
463      */
464     public void leaveFullscreen() {
465         if (mContent == null) return;
466         checkThreadSafety();
467         mContent.exitFullscreen();
468     }
469
470     /**
471      * Pause all layout, parsing and JavaScript timers for all XWalkView instances.
472      * Typically it should be called when the activity for this view is paused,
473      * and accordingly {@link #resumeTimers} should be called when the activity
474      * is resumed again.
475      *
476      * Note that it will globally impact all XWalkView instances, not limited to
477      * just this XWalkView.
478      */
479     public void pauseTimers() {
480         if (mContent == null) return;
481         checkThreadSafety();
482         mContent.pauseTimers();
483     }
484
485     /**
486      * Resume all layout, parsing and JavaScript timers for all XWalkView instances.
487      * Typically it should be called when the activity for this view is resumed.
488      *
489      * Note that it will globally impact all XWalkView instances, not limited to
490      * just this XWalkView.
491      */
492     public void resumeTimers() {
493         if (mContent == null) return;
494         checkThreadSafety();
495         mContent.resumeTimers();
496     }
497
498     /**
499      * Pause many other things except JavaScript timers inside rendering engine,
500      * like video player, modal dialogs, etc. See {@link #pauseTimers} about pausing
501      * JavaScript timers.
502      * Typically it should be called when the activity for this view is paused.
503      */
504     public void onHide() {
505         if (mContent == null || mIsHidden) return;
506         mExtensionManager.onPause();
507         mContent.onPause();
508         mIsHidden = true;
509     }
510
511     /**
512      * Resume video player, modal dialogs. Embedders are in charge of calling
513      * this during resuming this activity if they call onHide.
514      * Typically it should be called when the activity for this view is resumed.
515      */
516     public void onShow() {
517         if (mContent == null || !mIsHidden ) return;
518         mExtensionManager.onResume();
519         mContent.onResume();
520         mIsHidden = false;
521     }
522
523     /**
524      * Release internal resources occupied by this XWalkView.
525      */
526     public void onDestroy() {
527         destroy();
528     }
529
530     /**
531      * Pass through activity result to XWalkView. Many internal facilities need this
532      * to handle activity result like JavaScript dialog, Crosswalk extensions, etc.
533      * See <a href="http://developer.android.com/reference/android/app/Activity.html">
534      * android.app.Activity.onActivityResult()</a>.
535      * @param requestCode passed from android.app.Activity.onActivityResult().
536      * @param resultCode passed from android.app.Activity.onActivityResult().
537      * @param data passed from android.app.Activity.onActivityResult().
538      */
539     public void onActivityResult(int requestCode, int resultCode, Intent data) {
540         if (mContent == null) return;
541         mExtensionManager.onActivityResult(requestCode, resultCode, data);
542         mContent.onActivityResult(requestCode, resultCode, data);
543     }
544
545     /**
546      * Pass through intents to XWalkView. Many internal facilities need this
547      * to receive the intents like web notification. See
548      * <a href="http://developer.android.com/reference/android/app/Activity.html">
549      * android.app.Activity.onNewIntent()</a>.
550      * @param intent passed from android.app.Activity.onNewIntent().
551      */
552     public boolean onNewIntent(Intent intent) {
553         if (mContent == null) return false;
554         return mContent.onNewIntent(intent);
555     }
556
557     /**
558      * Save current internal state of this XWalkView. This can help restore this state
559      * afterwards restoring.
560      * @param outState the saved state for restoring.
561      */
562     public boolean saveState(Bundle outState) {
563         if (mContent == null) return false;
564         mContent.saveState(outState);
565         return true;
566     }
567
568     /**
569      * Restore the state from the saved bundle data.
570      * @param inState the state saved from saveState().
571      * @return true if it can restore the state.
572      */
573     public boolean restoreState(Bundle inState) {
574         if (mContent == null) return false;
575         if (mContent.restoreState(inState) != null) return true;
576         return false;
577     }
578
579     /**
580      * Get the API version of Crosswalk embedding API.
581      * @return the string of API level.
582      */
583     // TODO(yongsheng): make it static?
584     public String getAPIVersion() {
585         return "2.0";
586     }
587
588     /**
589      * Get the Crosswalk version.
590      * @return the string of Crosswalk.
591      */
592     // TODO(yongsheng): make it static?
593     public String getXWalkVersion() {
594         if (mContent == null) return null;
595         return mContent.getXWalkVersion();
596     }
597
598     /**
599      * Embedders use this to customize their handlers to events/callbacks related
600      * to UI.
601      * @param client the XWalkUIClient defined by callers.
602      */
603     public void setUIClient(XWalkUIClient client) {
604         if (mContent == null) return;
605         checkThreadSafety();
606         mContent.setUIClient(client);
607     }
608
609     /**
610      * Embedders use this to customize their handlers to events/callbacks related
611      * to resource loading.
612      * @param client the XWalkResourceClient defined by callers.
613      */
614     public void setResourceClient(XWalkResourceClient client) {
615         if (mContent == null) return;
616         checkThreadSafety();
617         mContent.setResourceClient(client);
618     }
619
620     /**
621      * Inherit from <a href="http://developer.android.com/reference/android/view/View.html">
622      * android.view.View</a>. This class needs to handle some keys like
623      * 'BACK'.
624      * @param keyCode passed from android.view.View.onKeyUp().
625      * @param event passed from android.view.View.onKeyUp().
626      */
627     @Override
628     public boolean onKeyUp(int keyCode, KeyEvent event) {
629         if (keyCode == KeyEvent.KEYCODE_BACK) {
630             // If there's navigation happens when app is fullscreen,
631             // the content will still be fullscreen after navigation.
632             // In such case, the back key will exit fullscreen first.
633             if (hasEnteredFullscreen()) {
634                 leaveFullscreen();
635                 return true;
636             } else if (canGoBack()) {
637                 goBack();
638                 return true;
639             }
640         }
641         return false;
642     }
643
644     // TODO(yongsheng): this is not public.
645     /**
646      * @hide
647      */
648     public XWalkSettings getSettings() {
649         if (mContent == null) return null;
650         checkThreadSafety();
651         return mContent.getSettings();
652     }
653
654     /**
655      * This method is used by Cordova for hacking.
656      * TODO(yongsheng): remove this and related test cases?
657      *
658      * @hide
659      */
660     public void setNetworkAvailable(boolean networkUp) {
661         if (mContent == null) return;
662         checkThreadSafety();
663         mContent.setNetworkAvailable(networkUp);
664     }
665
666     /**
667      * Enables remote debugging and returns the URL at which the dev tools server is listening
668      * for commands. The allowedUid argument can be used to specify the uid of the process that is
669      * permitted to connect.
670      * TODO(yongsheng): how to enable this in XWalkPreferences?
671      *
672      * @hide
673      */
674     public String enableRemoteDebugging(int allowedUid) {
675         if (mContent == null) return null;
676         checkThreadSafety();
677         return mContent.enableRemoteDebugging(allowedUid);
678     }
679
680     /**
681      * It's used for Presentation API.
682      * @hide
683      */
684     public int getContentID() {
685         if (mContent == null) return -1;
686         return mContent.getRoutingID();
687     }
688
689     boolean canGoBack() {
690         if (mContent == null) return false;
691         checkThreadSafety();
692         return mContent.canGoBack();
693     }
694
695     void goBack() {
696         if (mContent == null) return;
697         checkThreadSafety();
698         mContent.goBack();
699     }
700
701     boolean canGoForward() {
702         if (mContent == null) return false;
703         checkThreadSafety();
704         return mContent.canGoForward();
705     }
706
707     void goForward() {
708         if (mContent == null) return;
709         checkThreadSafety();
710         mContent.goForward();
711     }
712
713     void clearHistory() {
714         if (mContent == null) return;
715         checkThreadSafety();
716         mContent.clearHistory();
717     }
718
719     void destroy() {
720         if (mContent == null) return;
721         mExtensionManager.onDestroy();
722         mContent.destroy();
723         disableRemoteDebugging();
724     }
725
726     // Enables remote debugging and returns the URL at which the dev tools server is listening
727     // for commands. Only the current process is allowed to connect to the server.
728     String enableRemoteDebugging() {
729         return enableRemoteDebugging(mContext.getApplicationInfo().uid);
730     }
731
732     void disableRemoteDebugging() {
733         if (mContent == null) return;
734         checkThreadSafety();
735         mContent.disableRemoteDebugging();
736     }
737
738     private static void checkThreadSafety() {
739         if (Looper.myLooper() != Looper.getMainLooper()) {
740             Throwable throwable = new Throwable(
741                 "Warning: A XWalkView method was called on thread '" +
742                 Thread.currentThread().getName() + "'. " +
743                 "All XWalkView methods must be called on the UI thread. ");
744             throw new RuntimeException(throwable);
745         }
746     }
747
748     boolean isOwnerActivityRunning() {
749         int status = ApplicationStatus.getStateForActivity(getActivity());
750         if (status == ActivityState.DESTROYED) return false;
751         return true;
752     }
753
754     void navigateTo(int offset) {
755         if (mContent == null) return;
756         mContent.navigateTo(offset);
757     }
758
759     void setOverlayVideoMode(boolean enabled) {
760         mContent.setOverlayVideoMode(enabled);
761     }
762
763     // Below methods are for test shell and instrumentation tests.
764     /**
765      * @hide
766      */
767     public void setXWalkClient(XWalkClient client) {
768         if (mContent == null) return;
769         checkThreadSafety();
770         mContent.setXWalkClient(client);
771     }
772
773     /**
774      * @hide
775      */
776     public void setXWalkWebChromeClient(XWalkWebChromeClient client) {
777         if (mContent == null) return;
778         checkThreadSafety();
779         mContent.setXWalkWebChromeClient(client);
780     }
781
782     /**
783      * @hide
784      */
785     public void setDownloadListener(DownloadListener listener) {
786         if (mContent == null) return;
787         checkThreadSafety();
788         mContent.setDownloadListener(listener);
789     }
790
791     /**
792      * @hide
793      */
794     public void setNavigationHandler(XWalkNavigationHandler handler) {
795         if (mContent == null) return;
796         checkThreadSafety();
797         mContent.setNavigationHandler(handler);
798     }
799
800     /**
801      * @hide
802      */
803     public void setNotificationService(XWalkNotificationService service) {
804         if (mContent == null) return;
805         checkThreadSafety();
806         mContent.setNotificationService(service);
807     }
808 }