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