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