1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * vim: ts=4 sw=4 expandtab: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 package org.mozilla.gecko; 8 9 import java.util.Set; 10 11 import org.json.JSONException; 12 import org.json.JSONObject; 13 import org.mozilla.gecko.annotation.ReflectionTarget; 14 import org.mozilla.gecko.annotation.WrapForJNI; 15 import org.mozilla.gecko.gfx.LayerView; 16 import org.mozilla.gecko.mozglue.JNIObject; 17 import org.mozilla.gecko.util.EventCallback; 18 import org.mozilla.gecko.util.GeckoEventListener; 19 import org.mozilla.gecko.util.NativeEventListener; 20 import org.mozilla.gecko.util.NativeJSObject; 21 import org.mozilla.gecko.util.ThreadUtils; 22 23 import android.app.Activity; 24 import android.content.Context; 25 import android.content.SharedPreferences; 26 import android.os.Binder; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.util.AttributeSet; 33 import android.util.DisplayMetrics; 34 import android.util.Log; 35 import android.view.KeyEvent; 36 import android.view.View; 37 import android.view.inputmethod.EditorInfo; 38 import android.view.inputmethod.InputConnection; 39 40 public class GeckoView extends LayerView 41 implements ContextGetter, GeckoEventListener, NativeEventListener { 42 43 private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView"; 44 private static final String LOGTAG = "GeckoView"; 45 46 private ChromeDelegate mChromeDelegate; 47 private ContentDelegate mContentDelegate; 48 49 private InputConnectionListener mInputConnectionListener; 50 51 protected boolean onAttachedToWindowCalled; 52 protected String chromeURI = getGeckoInterface().getDefaultChromeURI(); 53 protected int screenId = 0; // default to the primary screen 54 55 @Override handleMessage(final String event, final JSONObject message)56 public void handleMessage(final String event, final JSONObject message) { 57 ThreadUtils.postToUiThread(new Runnable() { 58 @Override 59 public void run() { 60 try { 61 if (event.equals("Gecko:Ready")) { 62 handleReady(message); 63 } else if (event.equals("Content:StateChange")) { 64 handleStateChange(message); 65 } else if (event.equals("Content:LoadError")) { 66 handleLoadError(message); 67 } else if (event.equals("Content:PageShow")) { 68 handlePageShow(message); 69 } else if (event.equals("DOMTitleChanged")) { 70 handleTitleChanged(message); 71 } else if (event.equals("Link:Favicon")) { 72 handleLinkFavicon(message); 73 } else if (event.equals("Prompt:Show") || event.equals("Prompt:ShowTop")) { 74 handlePrompt(message); 75 } else if (event.equals("Accessibility:Event")) { 76 int mode = getImportantForAccessibility(); 77 if (mode == View.IMPORTANT_FOR_ACCESSIBILITY_YES || 78 mode == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 79 GeckoAccessibility.sendAccessibilityEvent(message); 80 } 81 } 82 } catch (Exception e) { 83 Log.e(LOGTAG, "handleMessage threw for " + event, e); 84 } 85 } 86 }); 87 } 88 89 @Override handleMessage(final String event, final NativeJSObject message, final EventCallback callback)90 public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) { 91 try { 92 if ("Accessibility:Ready".equals(event)) { 93 GeckoAccessibility.updateAccessibilitySettings(getContext()); 94 } else if ("GeckoView:Message".equals(event)) { 95 // We need to pull out the bundle while on the Gecko thread. 96 NativeJSObject json = message.optObject("data", null); 97 if (json == null) { 98 // Must have payload to call the message handler. 99 return; 100 } 101 final Bundle data = json.toBundle(); 102 ThreadUtils.postToUiThread(new Runnable() { 103 @Override 104 public void run() { 105 handleScriptMessage(data, callback); 106 } 107 }); 108 } 109 } catch (Exception e) { 110 Log.w(LOGTAG, "handleMessage threw for " + event, e); 111 } 112 } 113 114 @WrapForJNI(dispatchTo = "proxy") 115 protected static final class Window extends JNIObject { 116 @WrapForJNI(skip = true) Window()117 /* package */ Window() {} 118 open(Window instance, GeckoView view, Object compositor, String chromeURI, int screenId)119 static native void open(Window instance, GeckoView view, Object compositor, 120 String chromeURI, int screenId); 121 disposeNative()122 @Override protected native void disposeNative(); close()123 native void close(); reattach(GeckoView view, Object compositor)124 native void reattach(GeckoView view, Object compositor); loadUri(String uri, int flags)125 native void loadUri(String uri, int flags); 126 } 127 128 // Object to hold onto our nsWindow connection when GeckoView gets destroyed. 129 private static class StateBinder extends Binder implements Parcelable { 130 public final Parcelable superState; 131 public final Window window; 132 StateBinder(Parcelable superState, Window window)133 public StateBinder(Parcelable superState, Window window) { 134 this.superState = superState; 135 this.window = window; 136 } 137 138 @Override describeContents()139 public int describeContents() { 140 return 0; 141 } 142 143 @Override writeToParcel(Parcel out, int flags)144 public void writeToParcel(Parcel out, int flags) { 145 // Always write out the super-state, so that even if we lose this binder, we 146 // will still have something to pass into super.onRestoreInstanceState. 147 out.writeParcelable(superState, flags); 148 out.writeStrongBinder(this); 149 } 150 151 @ReflectionTarget 152 public static final Parcelable.Creator<StateBinder> CREATOR 153 = new Parcelable.Creator<StateBinder>() { 154 @Override 155 public StateBinder createFromParcel(Parcel in) { 156 final Parcelable superState = in.readParcelable(null); 157 final IBinder binder = in.readStrongBinder(); 158 if (binder instanceof StateBinder) { 159 return (StateBinder) binder; 160 } 161 // Not the original object we saved; return null state. 162 return new StateBinder(superState, null); 163 } 164 165 @Override 166 public StateBinder[] newArray(int size) { 167 return new StateBinder[size]; 168 } 169 }; 170 } 171 172 protected Window window; 173 private boolean stateSaved; 174 GeckoView(Context context)175 public GeckoView(Context context) { 176 super(context); 177 init(context); 178 } 179 GeckoView(Context context, AttributeSet attrs)180 public GeckoView(Context context, AttributeSet attrs) { 181 super(context, attrs); 182 init(context); 183 } 184 init(Context context)185 private void init(Context context) { 186 if (GeckoAppShell.getApplicationContext() == null) { 187 GeckoAppShell.setApplicationContext(context.getApplicationContext()); 188 } 189 190 // Set the GeckoInterface if the context is an activity and the GeckoInterface 191 // has not already been set 192 if (context instanceof Activity && getGeckoInterface() == null) { 193 setGeckoInterface(new BaseGeckoInterface(context)); 194 GeckoAppShell.setContextGetter(this); 195 } 196 197 // Perform common initialization for Fennec/GeckoView. 198 GeckoAppShell.setLayerView(this); 199 200 initializeView(EventDispatcher.getInstance()); 201 } 202 203 @Override onSaveInstanceState()204 protected Parcelable onSaveInstanceState() 205 { 206 final Parcelable superState = super.onSaveInstanceState(); 207 stateSaved = true; 208 return new StateBinder(superState, this.window); 209 } 210 211 @Override onRestoreInstanceState(final Parcelable state)212 protected void onRestoreInstanceState(final Parcelable state) 213 { 214 final StateBinder stateBinder = (StateBinder) state; 215 216 if (stateBinder.window != null) { 217 this.window = stateBinder.window; 218 } 219 stateSaved = false; 220 221 if (onAttachedToWindowCalled) { 222 reattachWindow(); 223 } 224 225 // We have to always call super.onRestoreInstanceState because View keeps 226 // track of these calls and throws an exception when we don't call it. 227 super.onRestoreInstanceState(stateBinder.superState); 228 } 229 openWindow()230 protected void openWindow() { 231 232 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 233 Window.open(window, this, getCompositor(), 234 chromeURI, screenId); 235 } else { 236 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, Window.class, 237 "open", window, GeckoView.class, this, Object.class, getCompositor(), 238 String.class, chromeURI, screenId); 239 } 240 } 241 reattachWindow()242 protected void reattachWindow() { 243 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 244 window.reattach(this, getCompositor()); 245 } else { 246 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 247 window, "reattach", GeckoView.class, this, Object.class, getCompositor()); 248 } 249 } 250 251 @Override onAttachedToWindow()252 public void onAttachedToWindow() 253 { 254 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); 255 256 if (window == null) { 257 // Open a new nsWindow if we didn't have one from before. 258 window = new Window(); 259 openWindow(); 260 } else { 261 reattachWindow(); 262 } 263 264 super.onAttachedToWindow(); 265 266 onAttachedToWindowCalled = true; 267 } 268 269 @Override onDetachedFromWindow()270 public void onDetachedFromWindow() 271 { 272 super.onDetachedFromWindow(); 273 super.destroy(); 274 275 if (stateSaved) { 276 // If we saved state earlier, we don't want to close the nsWindow. 277 return; 278 } 279 280 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 281 window.close(); 282 window.disposeNative(); 283 } else { 284 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 285 window, "close"); 286 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 287 window, "disposeNative"); 288 } 289 290 onAttachedToWindowCalled = false; 291 } 292 293 @WrapForJNI public static final int LOAD_DEFAULT = 0; 294 @WrapForJNI public static final int LOAD_NEW_TAB = 1; 295 @WrapForJNI public static final int LOAD_SWITCH_TAB = 2; 296 loadUri(String uri, int flags)297 public void loadUri(String uri, int flags) { 298 if (window == null) { 299 throw new IllegalStateException("Not attached to window"); 300 } 301 302 if (GeckoThread.isRunning()) { 303 window.loadUri(uri, flags); 304 } else { 305 GeckoThread.queueNativeCall(window, "loadUri", String.class, uri, flags); 306 } 307 } 308 setInputConnectionListener(final InputConnectionListener icl)309 /* package */ void setInputConnectionListener(final InputConnectionListener icl) { 310 mInputConnectionListener = icl; 311 } 312 313 @Override getHandler()314 public Handler getHandler() { 315 if (mInputConnectionListener != null) { 316 return mInputConnectionListener.getHandler(super.getHandler()); 317 } 318 return super.getHandler(); 319 } 320 321 @Override onCreateInputConnection(EditorInfo outAttrs)322 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 323 if (mInputConnectionListener != null) { 324 return mInputConnectionListener.onCreateInputConnection(outAttrs); 325 } 326 return null; 327 } 328 329 @Override onKeyPreIme(int keyCode, KeyEvent event)330 public boolean onKeyPreIme(int keyCode, KeyEvent event) { 331 if (super.onKeyPreIme(keyCode, event)) { 332 return true; 333 } 334 return mInputConnectionListener != null && 335 mInputConnectionListener.onKeyPreIme(keyCode, event); 336 } 337 338 @Override onKeyUp(int keyCode, KeyEvent event)339 public boolean onKeyUp(int keyCode, KeyEvent event) { 340 if (super.onKeyUp(keyCode, event)) { 341 return true; 342 } 343 return mInputConnectionListener != null && 344 mInputConnectionListener.onKeyUp(keyCode, event); 345 } 346 347 @Override onKeyDown(int keyCode, KeyEvent event)348 public boolean onKeyDown(int keyCode, KeyEvent event) { 349 if (super.onKeyDown(keyCode, event)) { 350 return true; 351 } 352 return mInputConnectionListener != null && 353 mInputConnectionListener.onKeyDown(keyCode, event); 354 } 355 356 @Override onKeyLongPress(int keyCode, KeyEvent event)357 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 358 if (super.onKeyLongPress(keyCode, event)) { 359 return true; 360 } 361 return mInputConnectionListener != null && 362 mInputConnectionListener.onKeyLongPress(keyCode, event); 363 } 364 365 @Override onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)366 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 367 if (super.onKeyMultiple(keyCode, repeatCount, event)) { 368 return true; 369 } 370 return mInputConnectionListener != null && 371 mInputConnectionListener.onKeyMultiple(keyCode, repeatCount, event); 372 } 373 isIMEEnabled()374 /* package */ boolean isIMEEnabled() { 375 return mInputConnectionListener != null && 376 mInputConnectionListener.isIMEEnabled(); 377 } 378 importScript(final String url)379 public void importScript(final String url) { 380 if (url.startsWith("resource://android/assets/")) { 381 GeckoAppShell.notifyObservers("GeckoView:ImportScript", url); 382 return; 383 } 384 385 throw new IllegalArgumentException("Must import script from 'resources://android/assets/' location."); 386 } 387 handleReady(final JSONObject message)388 private void handleReady(final JSONObject message) { 389 if (mChromeDelegate != null) { 390 mChromeDelegate.onReady(this); 391 } 392 } 393 handleStateChange(final JSONObject message)394 private void handleStateChange(final JSONObject message) throws JSONException { 395 int state = message.getInt("state"); 396 if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { 397 if ((state & GeckoAppShell.WPL_STATE_START) != 0) { 398 if (mContentDelegate != null) { 399 int id = message.getInt("tabID"); 400 mContentDelegate.onPageStart(this, new Browser(id), message.getString("uri")); 401 } 402 } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { 403 if (mContentDelegate != null) { 404 int id = message.getInt("tabID"); 405 mContentDelegate.onPageStop(this, new Browser(id), message.getBoolean("success")); 406 } 407 } 408 } 409 } 410 handleLoadError(final JSONObject message)411 private void handleLoadError(final JSONObject message) throws JSONException { 412 if (mContentDelegate != null) { 413 int id = message.getInt("tabID"); 414 mContentDelegate.onPageStop(GeckoView.this, new Browser(id), false); 415 } 416 } 417 handlePageShow(final JSONObject message)418 private void handlePageShow(final JSONObject message) throws JSONException { 419 if (mContentDelegate != null) { 420 int id = message.getInt("tabID"); 421 mContentDelegate.onPageShow(GeckoView.this, new Browser(id)); 422 } 423 } 424 handleTitleChanged(final JSONObject message)425 private void handleTitleChanged(final JSONObject message) throws JSONException { 426 if (mContentDelegate != null) { 427 int id = message.getInt("tabID"); 428 mContentDelegate.onReceivedTitle(GeckoView.this, new Browser(id), message.getString("title")); 429 } 430 } 431 handleLinkFavicon(final JSONObject message)432 private void handleLinkFavicon(final JSONObject message) throws JSONException { 433 if (mContentDelegate != null) { 434 int id = message.getInt("tabID"); 435 mContentDelegate.onReceivedFavicon(GeckoView.this, new Browser(id), message.getString("href"), message.getInt("size")); 436 } 437 } 438 handlePrompt(final JSONObject message)439 private void handlePrompt(final JSONObject message) throws JSONException { 440 if (mChromeDelegate != null) { 441 String hint = message.optString("hint"); 442 if ("alert".equals(hint)) { 443 String text = message.optString("text"); 444 mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message)); 445 } else if ("confirm".equals(hint)) { 446 String text = message.optString("text"); 447 mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message)); 448 } else if ("prompt".equals(hint)) { 449 String text = message.optString("text"); 450 String defaultValue = message.optString("textbox0"); 451 mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message)); 452 } else if ("remotedebug".equals(hint)) { 453 mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message)); 454 } 455 } 456 } 457 handleScriptMessage(final Bundle data, final EventCallback callback)458 private void handleScriptMessage(final Bundle data, final EventCallback callback) { 459 if (mChromeDelegate != null) { 460 MessageResult result = null; 461 if (callback != null) { 462 result = new MessageResult(callback); 463 } 464 mChromeDelegate.onScriptMessage(GeckoView.this, data, result); 465 } 466 } 467 468 /** 469 * Set the chrome callback handler. 470 * This will replace the current handler. 471 * @param chrome An implementation of GeckoViewChrome. 472 */ setChromeDelegate(ChromeDelegate chrome)473 public void setChromeDelegate(ChromeDelegate chrome) { 474 mChromeDelegate = chrome; 475 } 476 477 /** 478 * Set the content callback handler. 479 * This will replace the current handler. 480 * @param content An implementation of ContentDelegate. 481 */ setContentDelegate(ContentDelegate content)482 public void setContentDelegate(ContentDelegate content) { 483 mContentDelegate = content; 484 } 485 setGeckoInterface(final BaseGeckoInterface geckoInterface)486 public static void setGeckoInterface(final BaseGeckoInterface geckoInterface) { 487 GeckoAppShell.setGeckoInterface(geckoInterface); 488 } 489 getGeckoInterface()490 public static GeckoAppShell.GeckoInterface getGeckoInterface() { 491 return GeckoAppShell.getGeckoInterface(); 492 } 493 getSharedPreferencesFile()494 protected String getSharedPreferencesFile() { 495 return DEFAULT_SHARED_PREFERENCES_FILE; 496 } 497 498 @Override getSharedPreferences()499 public SharedPreferences getSharedPreferences() { 500 return getContext().getSharedPreferences(getSharedPreferencesFile(), 0); 501 } 502 503 /** 504 * Wrapper for a browser in the GeckoView container. Associated with a browser 505 * element in the Gecko system. 506 */ 507 public class Browser { 508 private final int mId; Browser(int Id)509 private Browser(int Id) { 510 mId = Id; 511 } 512 513 /** 514 * Get the ID of the Browser. This is the same ID used by Gecko for it's underlying 515 * browser element. 516 * @return The integer ID of the Browser. 517 */ getId()518 private int getId() { 519 return mId; 520 } 521 522 /** 523 * Load a URL resource into the Browser. 524 * @param url The URL string. 525 */ loadUrl(String url)526 public void loadUrl(String url) { 527 JSONObject args = new JSONObject(); 528 try { 529 args.put("url", url); 530 args.put("parentId", -1); 531 args.put("newTab", false); 532 args.put("tabID", mId); 533 } catch (Exception e) { 534 Log.w(LOGTAG, "Error building JSON arguments for loadUrl.", e); 535 } 536 GeckoAppShell.notifyObservers("Tab:Load", args.toString()); 537 } 538 } 539 540 /* Provides a means for the client to indicate whether a JavaScript 541 * dialog request should proceed. An instance of this class is passed to 542 * various GeckoViewChrome callback actions. 543 */ 544 public class PromptResult { 545 private final int RESULT_OK = 0; 546 private final int RESULT_CANCEL = 1; 547 548 private final JSONObject mMessage; 549 PromptResult(JSONObject message)550 public PromptResult(JSONObject message) { 551 mMessage = message; 552 } 553 makeResult(int resultCode)554 private JSONObject makeResult(int resultCode) { 555 JSONObject result = new JSONObject(); 556 try { 557 result.put("button", resultCode); 558 } catch (JSONException ex) { } 559 return result; 560 } 561 562 /** 563 * Handle a confirmation response from the user. 564 */ confirm()565 public void confirm() { 566 JSONObject result = makeResult(RESULT_OK); 567 EventDispatcher.sendResponse(mMessage, result); 568 } 569 570 /** 571 * Handle a confirmation response from the user. 572 * @param value String value to return to the browser context. 573 */ confirmWithValue(String value)574 public void confirmWithValue(String value) { 575 JSONObject result = makeResult(RESULT_OK); 576 try { 577 result.put("textbox0", value); 578 } catch (JSONException ex) { } 579 EventDispatcher.sendResponse(mMessage, result); 580 } 581 582 /** 583 * Handle a cancellation response from the user. 584 */ cancel()585 public void cancel() { 586 JSONObject result = makeResult(RESULT_CANCEL); 587 EventDispatcher.sendResponse(mMessage, result); 588 } 589 } 590 591 /* Provides a means for the client to respond to a script message with some data. 592 * An instance of this class is passed to GeckoViewChrome.onScriptMessage. 593 */ 594 public class MessageResult { 595 private final EventCallback mCallback; 596 MessageResult(EventCallback callback)597 public MessageResult(EventCallback callback) { 598 if (callback == null) { 599 throw new IllegalArgumentException("EventCallback should not be null."); 600 } 601 mCallback = callback; 602 } 603 bundleToJSON(Bundle data)604 private JSONObject bundleToJSON(Bundle data) { 605 JSONObject result = new JSONObject(); 606 if (data == null) { 607 return result; 608 } 609 610 final Set<String> keys = data.keySet(); 611 for (String key : keys) { 612 try { 613 result.put(key, data.get(key)); 614 } catch (JSONException e) { 615 } 616 } 617 return result; 618 } 619 620 /** 621 * Handle a successful response to a script message. 622 * @param value Bundle value to return to the script context. 623 */ success(Bundle data)624 public void success(Bundle data) { 625 mCallback.sendSuccess(bundleToJSON(data)); 626 } 627 628 /** 629 * Handle a failure response to a script message. 630 */ failure(Bundle data)631 public void failure(Bundle data) { 632 mCallback.sendError(bundleToJSON(data)); 633 } 634 } 635 636 public interface ChromeDelegate { 637 /** 638 * Tell the host application that Gecko is ready to handle requests. 639 * @param view The GeckoView that initiated the callback. 640 */ onReady(GeckoView view)641 public void onReady(GeckoView view); 642 643 /** 644 * Tell the host application to display an alert dialog. 645 * @param view The GeckoView that initiated the callback. 646 * @param browser The Browser that is loading the content. 647 * @param message The string to display in the dialog. 648 * @param result A PromptResult used to send back the result without blocking. 649 * Defaults to cancel requests. 650 */ onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result)651 public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result); 652 653 /** 654 * Tell the host application to display a confirmation dialog. 655 * @param view The GeckoView that initiated the callback. 656 * @param browser The Browser that is loading the content. 657 * @param message The string to display in the dialog. 658 * @param result A PromptResult used to send back the result without blocking. 659 * Defaults to cancel requests. 660 */ onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result)661 public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result); 662 663 /** 664 * Tell the host application to display an input prompt dialog. 665 * @param view The GeckoView that initiated the callback. 666 * @param browser The Browser that is loading the content. 667 * @param message The string to display in the dialog. 668 * @param defaultValue The string to use as default input. 669 * @param result A PromptResult used to send back the result without blocking. 670 * Defaults to cancel requests. 671 */ onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result)672 public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result); 673 674 /** 675 * Tell the host application to display a remote debugging request dialog. 676 * @param view The GeckoView that initiated the callback. 677 * @param result A PromptResult used to send back the result without blocking. 678 * Defaults to cancel requests. 679 */ onDebugRequest(GeckoView view, GeckoView.PromptResult result)680 public void onDebugRequest(GeckoView view, GeckoView.PromptResult result); 681 682 /** 683 * Receive a message from an imported script. 684 * @param view The GeckoView that initiated the callback. 685 * @param data Bundle of data sent with the message. Never null. 686 * @param result A MessageResult used to send back a response without blocking. Can be null. 687 * Defaults to do nothing. 688 */ onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result)689 public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result); 690 } 691 692 public interface ContentDelegate { 693 /** 694 * A Browser has started loading content from the network. 695 * @param view The GeckoView that initiated the callback. 696 * @param browser The Browser that is loading the content. 697 * @param url The resource being loaded. 698 */ onPageStart(GeckoView view, GeckoView.Browser browser, String url)699 public void onPageStart(GeckoView view, GeckoView.Browser browser, String url); 700 701 /** 702 * A Browser has finished loading content from the network. 703 * @param view The GeckoView that initiated the callback. 704 * @param browser The Browser that was loading the content. 705 * @param success Whether the page loaded successfully or an error occurred. 706 */ onPageStop(GeckoView view, GeckoView.Browser browser, boolean success)707 public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success); 708 709 /** 710 * A Browser is displaying content. This page could have been loaded via 711 * network or from the session history. 712 * @param view The GeckoView that initiated the callback. 713 * @param browser The Browser that is showing the content. 714 */ onPageShow(GeckoView view, GeckoView.Browser browser)715 public void onPageShow(GeckoView view, GeckoView.Browser browser); 716 717 /** 718 * A page title was discovered in the content or updated after the content 719 * loaded. 720 * @param view The GeckoView that initiated the callback. 721 * @param browser The Browser that is showing the content. 722 * @param title The title sent from the content. 723 */ onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title)724 public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title); 725 726 /** 727 * A link element was discovered in the content or updated after the content 728 * loaded that specifies a favicon. 729 * @param view The GeckoView that initiated the callback. 730 * @param browser The Browser that is showing the content. 731 * @param url The href of the link element specifying the favicon. 732 * @param size The maximum size specified for the favicon, or -1 for any size. 733 */ onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size)734 public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size); 735 } 736 } 737