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.geckoview; 8 9 import java.net.URLConnection; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.UUID; 13 14 import org.mozilla.gecko.annotation.WrapForJNI; 15 import org.mozilla.gecko.EventDispatcher; 16 import org.mozilla.gecko.gfx.LayerSession; 17 import org.mozilla.gecko.GeckoAppShell; 18 import org.mozilla.gecko.GeckoEditableChild; 19 import org.mozilla.gecko.GeckoThread; 20 import org.mozilla.gecko.IGeckoEditableParent; 21 import org.mozilla.gecko.mozglue.JNIObject; 22 import org.mozilla.gecko.NativeQueue; 23 import org.mozilla.gecko.util.BundleEventListener; 24 import org.mozilla.gecko.util.EventCallback; 25 import org.mozilla.gecko.util.GeckoBundle; 26 import org.mozilla.gecko.util.ThreadUtils; 27 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.res.Resources; 31 import android.database.Cursor; 32 import android.net.Uri; 33 import android.os.Binder; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.IInterface; 37 import android.os.Parcel; 38 import android.os.Parcelable; 39 import android.os.SystemClock; 40 import android.support.annotation.Nullable; 41 import android.support.annotation.NonNull; 42 import android.util.Log; 43 44 public class GeckoSession extends LayerSession 45 implements Parcelable { 46 private static final String LOGTAG = "GeckoSession"; 47 private static final boolean DEBUG = false; 48 49 // Type of changes given to onWindowChanged. 50 // Window has been cleared due to the session being closed. 51 private static final int WINDOW_CLOSE = 0; 52 // Window has been set due to the session being opened. 53 private static final int WINDOW_OPEN = 1; // Window has been opened. 54 // Window has been cleared due to the session being transferred to another session. 55 private static final int WINDOW_TRANSFER_OUT = 2; // Window has been transfer. 56 // Window has been set due to another session being transferred to this one. 57 private static final int WINDOW_TRANSFER_IN = 3; 58 59 private enum State implements NativeQueue.State { 60 INITIAL(0), 61 READY(1); 62 63 private final int mRank; 64 State(int rank)65 private State(int rank) { 66 mRank = rank; 67 } 68 69 @Override is(final NativeQueue.State other)70 public boolean is(final NativeQueue.State other) { 71 return this == other; 72 } 73 74 @Override isAtLeast(final NativeQueue.State other)75 public boolean isAtLeast(final NativeQueue.State other) { 76 return (other instanceof State) && 77 mRank >= ((State) other).mRank; 78 } 79 } 80 81 private final NativeQueue mNativeQueue = 82 new NativeQueue(State.INITIAL, State.READY); 83 84 private final EventDispatcher mEventDispatcher = 85 new EventDispatcher(mNativeQueue); 86 87 private final TextInputController mTextInput = new TextInputController(this, mNativeQueue); 88 89 private String mId = UUID.randomUUID().toString().replace("-", ""); getId()90 /* package */ String getId() { return mId; } 91 92 private final GeckoSessionHandler<ContentDelegate> mContentHandler = 93 new GeckoSessionHandler<ContentDelegate>( 94 "GeckoViewContent", this, 95 new String[]{ 96 "GeckoView:ContextMenu", 97 "GeckoView:DOMTitleChanged", 98 "GeckoView:DOMWindowFocus", 99 "GeckoView:DOMWindowClose", 100 "GeckoView:FullScreenEnter", 101 "GeckoView:FullScreenExit" 102 } 103 ) { 104 @Override 105 public void handleMessage(final ContentDelegate delegate, 106 final String event, 107 final GeckoBundle message, 108 final EventCallback callback) { 109 110 if ("GeckoView:ContextMenu".equals(event)) { 111 delegate.onContextMenu(GeckoSession.this, 112 message.getInt("screenX"), 113 message.getInt("screenY"), 114 message.getString("uri"), 115 message.getString("elementSrc")); 116 } else if ("GeckoView:DOMTitleChanged".equals(event)) { 117 delegate.onTitleChange(GeckoSession.this, 118 message.getString("title")); 119 } else if ("GeckoView:DOMWindowFocus".equals(event)) { 120 delegate.onFocusRequest(GeckoSession.this); 121 } else if ("GeckoView:DOMWindowClose".equals(event)) { 122 delegate.onCloseRequest(GeckoSession.this); 123 } else if ("GeckoView:FullScreenEnter".equals(event)) { 124 delegate.onFullScreen(GeckoSession.this, true); 125 } else if ("GeckoView:FullScreenExit".equals(event)) { 126 delegate.onFullScreen(GeckoSession.this, false); 127 } 128 } 129 }; 130 131 private final GeckoSessionHandler<NavigationDelegate> mNavigationHandler = 132 new GeckoSessionHandler<NavigationDelegate>( 133 "GeckoViewNavigation", this, 134 new String[]{ 135 "GeckoView:LocationChange", 136 "GeckoView:OnLoadRequest", 137 "GeckoView:OnNewSession" 138 } 139 ) { 140 // This needs to match nsIBrowserDOMWindow.idl 141 private int convertGeckoTarget(int geckoTarget) { 142 switch (geckoTarget) { 143 case 0: // OPEN_DEFAULTWINDOW 144 case 1: // OPEN_CURRENTWINDOW 145 return NavigationDelegate.TARGET_WINDOW_CURRENT; 146 default: // OPEN_NEWWINDOW, OPEN_NEWTAB, OPEN_SWITCHTAB 147 return NavigationDelegate.TARGET_WINDOW_NEW; 148 } 149 } 150 151 @Override 152 public void handleMessage(final NavigationDelegate delegate, 153 final String event, 154 final GeckoBundle message, 155 final EventCallback callback) { 156 if ("GeckoView:LocationChange".equals(event)) { 157 delegate.onLocationChange(GeckoSession.this, 158 message.getString("uri")); 159 delegate.onCanGoBack(GeckoSession.this, 160 message.getBoolean("canGoBack")); 161 delegate.onCanGoForward(GeckoSession.this, 162 message.getBoolean("canGoForward")); 163 } else if ("GeckoView:OnLoadRequest".equals(event)) { 164 final String uri = message.getString("uri"); 165 final int where = convertGeckoTarget(message.getInt("where")); 166 final boolean result = 167 delegate.onLoadRequest(GeckoSession.this, uri, where); 168 callback.sendSuccess(result); 169 } else if ("GeckoView:OnNewSession".equals(event)) { 170 final String uri = message.getString("uri"); 171 delegate.onNewSession(GeckoSession.this, uri, 172 new Response<GeckoSession>() { 173 @Override 174 public void respond(GeckoSession session) { 175 if (session == null) { 176 callback.sendSuccess(null); 177 return; 178 } 179 180 if (session.isOpen()) { 181 throw new IllegalArgumentException("Must use an unopened GeckoSession instance"); 182 } 183 184 session.open(null); 185 callback.sendSuccess(session.getId()); 186 } 187 }); 188 } 189 } 190 }; 191 192 private final GeckoSessionHandler<ProgressDelegate> mProgressHandler = 193 new GeckoSessionHandler<ProgressDelegate>( 194 "GeckoViewProgress", this, 195 new String[]{ 196 "GeckoView:PageStart", 197 "GeckoView:PageStop", 198 "GeckoView:SecurityChanged" 199 } 200 ) { 201 @Override 202 public void handleMessage(final ProgressDelegate delegate, 203 final String event, 204 final GeckoBundle message, 205 final EventCallback callback) { 206 if ("GeckoView:PageStart".equals(event)) { 207 delegate.onPageStart(GeckoSession.this, 208 message.getString("uri")); 209 } else if ("GeckoView:PageStop".equals(event)) { 210 delegate.onPageStop(GeckoSession.this, 211 message.getBoolean("success")); 212 } else if ("GeckoView:SecurityChanged".equals(event)) { 213 final GeckoBundle identity = message.getBundle("identity"); 214 delegate.onSecurityChange(GeckoSession.this, new ProgressDelegate.SecurityInformation(identity)); 215 } 216 } 217 }; 218 219 private final GeckoSessionHandler<ScrollDelegate> mScrollHandler = 220 new GeckoSessionHandler<ScrollDelegate>( 221 "GeckoViewScroll", this, 222 new String[]{ "GeckoView:ScrollChanged" } 223 ) { 224 @Override 225 public void handleMessage(final ScrollDelegate delegate, 226 final String event, 227 final GeckoBundle message, 228 final EventCallback callback) { 229 230 if ("GeckoView:ScrollChanged".equals(event)) { 231 delegate.onScrollChanged(GeckoSession.this, 232 message.getInt("scrollX"), 233 message.getInt("scrollY")); 234 } 235 } 236 }; 237 238 private final GeckoSessionHandler<TrackingProtectionDelegate> mTrackingProtectionHandler = 239 new GeckoSessionHandler<TrackingProtectionDelegate>( 240 "GeckoViewTrackingProtection", this, 241 new String[]{ "GeckoView:TrackingProtectionBlocked" } 242 ) { 243 @Override 244 public void handleMessage(final TrackingProtectionDelegate delegate, 245 final String event, 246 final GeckoBundle message, 247 final EventCallback callback) { 248 249 if ("GeckoView:TrackingProtectionBlocked".equals(event)) { 250 final String uri = message.getString("src"); 251 final String matchedList = message.getString("matchedList"); 252 delegate.onTrackerBlocked(GeckoSession.this, uri, 253 TrackingProtection.listToCategory(matchedList)); 254 } 255 } 256 }; 257 258 private final GeckoSessionHandler<PermissionDelegate> mPermissionHandler = 259 new GeckoSessionHandler<PermissionDelegate>( 260 "GeckoViewPermission", this, 261 new String[] { 262 "GeckoView:AndroidPermission", 263 "GeckoView:ContentPermission", 264 "GeckoView:MediaPermission" 265 }, /* alwaysListen */ true 266 ) { 267 @Override 268 public void handleMessage(final PermissionDelegate delegate, 269 final String event, 270 final GeckoBundle message, 271 final EventCallback callback) { 272 273 if (delegate == null) { 274 callback.sendSuccess(/* granted */ false); 275 return; 276 } 277 if ("GeckoView:AndroidPermission".equals(event)) { 278 delegate.onAndroidPermissionsRequest( 279 GeckoSession.this, message.getStringArray("perms"), 280 new PermissionCallback("android", callback)); 281 } else if ("GeckoView:ContentPermission".equals(event)) { 282 final String typeString = message.getString("perm"); 283 final int type; 284 if ("geolocation".equals(typeString)) { 285 type = PermissionDelegate.PERMISSION_GEOLOCATION; 286 } else if ("desktop_notification".equals(typeString)) { 287 type = PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION; 288 } else { 289 throw new IllegalArgumentException("Unknown permission request: " + typeString); 290 } 291 delegate.onContentPermissionRequest( 292 GeckoSession.this, message.getString("uri"), 293 type, message.getString("access"), 294 new PermissionCallback(typeString, callback)); 295 } else if ("GeckoView:MediaPermission".equals(event)) { 296 GeckoBundle[] videoBundles = message.getBundleArray("video"); 297 GeckoBundle[] audioBundles = message.getBundleArray("audio"); 298 PermissionDelegate.MediaSource[] videos = null; 299 PermissionDelegate.MediaSource[] audios = null; 300 301 if (videoBundles != null) { 302 videos = new PermissionDelegate.MediaSource[videoBundles.length]; 303 for (int i = 0; i < videoBundles.length; i++) { 304 videos[i] = new PermissionDelegate.MediaSource(videoBundles[i]); 305 } 306 } 307 308 if (audioBundles != null) { 309 audios = new PermissionDelegate.MediaSource[audioBundles.length]; 310 for (int i = 0; i < audioBundles.length; i++) { 311 audios[i] = new PermissionDelegate.MediaSource(audioBundles[i]); 312 } 313 } 314 315 delegate.onMediaPermissionRequest( 316 GeckoSession.this, message.getString("uri"), 317 videos, audios, new PermissionCallback("media", callback)); 318 } 319 } 320 }; 321 322 /* package */ int handlersCount; 323 324 private final GeckoSessionHandler<?>[] mSessionHandlers = new GeckoSessionHandler<?>[] { 325 mContentHandler, mNavigationHandler, mProgressHandler, mScrollHandler, 326 mTrackingProtectionHandler, mPermissionHandler 327 }; 328 329 private static class PermissionCallback implements 330 PermissionDelegate.Callback, PermissionDelegate.MediaCallback { 331 332 private final String mType; 333 private EventCallback mCallback; 334 PermissionCallback(final String type, final EventCallback callback)335 public PermissionCallback(final String type, final EventCallback callback) { 336 mType = type; 337 mCallback = callback; 338 } 339 submit(final Object response)340 private void submit(final Object response) { 341 if (mCallback != null) { 342 mCallback.sendSuccess(response); 343 mCallback = null; 344 } 345 } 346 347 @Override // PermissionDelegate.Callback grant()348 public void grant() { 349 if ("media".equals(mType)) { 350 throw new UnsupportedOperationException(); 351 } 352 submit(/* response */ true); 353 } 354 355 @Override // PermissionDelegate.Callback, PermissionDelegate.MediaCallback reject()356 public void reject() { 357 submit(/* response */ false); 358 } 359 360 @Override // PermissionDelegate.MediaCallback grant(final String video, final String audio)361 public void grant(final String video, final String audio) { 362 if (!"media".equals(mType)) { 363 throw new UnsupportedOperationException(); 364 } 365 final GeckoBundle response = new GeckoBundle(2); 366 response.putString("video", video); 367 response.putString("audio", audio); 368 submit(response); 369 } 370 371 @Override // PermissionDelegate.MediaCallback grant(final PermissionDelegate.MediaSource video, final PermissionDelegate.MediaSource audio)372 public void grant(final PermissionDelegate.MediaSource video, final PermissionDelegate.MediaSource audio) { 373 grant(video != null ? video.id : null, 374 audio != null ? audio.id : null); 375 } 376 } 377 378 /** 379 * Get the current prompt delegate for this GeckoSession. 380 * @return PromptDelegate instance or null if using default delegate. 381 */ getPermissionDelegate()382 public PermissionDelegate getPermissionDelegate() { 383 return mPermissionHandler.getDelegate(); 384 } 385 386 /** 387 * Set the current permission delegate for this GeckoSession. 388 * @param delegate PermissionDelegate instance or null to use the default delegate. 389 */ setPermissionDelegate(final PermissionDelegate delegate)390 public void setPermissionDelegate(final PermissionDelegate delegate) { 391 mPermissionHandler.setDelegate(delegate, this); 392 } 393 394 private PromptDelegate mPromptDelegate; 395 396 private final Listener mListener = new Listener(); 397 398 /* package */ static final class Window extends JNIObject implements IInterface { 399 private NativeQueue mNativeQueue; 400 private Binder mBinder; 401 Window(final NativeQueue nativeQueue)402 public Window(final NativeQueue nativeQueue) { 403 mNativeQueue = nativeQueue; 404 } 405 406 @Override // IInterface asBinder()407 public Binder asBinder() { 408 if (mBinder == null) { 409 mBinder = new Binder(); 410 mBinder.attachInterface(this, Window.class.getName()); 411 } 412 return mBinder; 413 } 414 415 // Create a new Gecko window and assign an initial set of Java session objects to it. 416 @WrapForJNI(dispatchTo = "proxy") open(Window instance, NativeQueue queue, Compositor compositor, EventDispatcher dispatcher, GeckoBundle settings, String id, String chromeUri, int screenId, boolean privateMode)417 public static native void open(Window instance, NativeQueue queue, 418 Compositor compositor, EventDispatcher dispatcher, 419 GeckoBundle settings, String id, String chromeUri, 420 int screenId, boolean privateMode); 421 422 @Override // JNIObject disposeNative()423 public void disposeNative() { 424 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 425 nativeDisposeNative(); 426 } else { 427 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 428 this, "nativeDisposeNative"); 429 } 430 } 431 432 @WrapForJNI(dispatchTo = "proxy", stubName = "DisposeNative") nativeDisposeNative()433 private native void nativeDisposeNative(); 434 435 // Force the underlying Gecko window to close and release assigned Java objects. close()436 public void close() { 437 // Reset our queue, so we don't end up with queued calls on a disposed object. 438 synchronized (this) { 439 if (mNativeQueue == null) { 440 // Already closed elsewhere. 441 return; 442 } 443 mNativeQueue.reset(State.INITIAL); 444 mNativeQueue = null; 445 } 446 447 // Detach ourselves from the binder as well, to prevent this window from being 448 // read from any parcels. 449 asBinder().attachInterface(null, Window.class.getName()); 450 451 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 452 nativeClose(); 453 } else { 454 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 455 this, "nativeClose"); 456 } 457 } 458 459 @WrapForJNI(dispatchTo = "proxy", stubName = "Close") nativeClose()460 private native void nativeClose(); 461 462 // Assign a new set of Java session objects to the underlying Gecko window. 463 // This replaces previously assigned objects from open() or transfer() calls. transfer(final NativeQueue queue, final Compositor compositor, final EventDispatcher dispatcher, final GeckoBundle settings)464 public synchronized void transfer(final NativeQueue queue, 465 final Compositor compositor, 466 final EventDispatcher dispatcher, 467 final GeckoBundle settings) { 468 if (mNativeQueue == null) { 469 // Already closed. 470 return; 471 } 472 473 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 474 nativeTransfer(queue, compositor, dispatcher, settings); 475 } else { 476 GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, 477 this, "nativeTransfer", 478 NativeQueue.class, queue, 479 Compositor.class, compositor, 480 EventDispatcher.class, dispatcher, 481 GeckoBundle.class, settings); 482 } 483 484 if (mNativeQueue != queue) { 485 // Reset the old queue to prevent old events from affecting this window. 486 // Gecko will call onReady later with the new queue if needed. 487 mNativeQueue.reset(State.INITIAL); 488 mNativeQueue = queue; 489 } 490 } 491 492 @WrapForJNI(dispatchTo = "proxy", stubName = "Transfer") nativeTransfer(NativeQueue queue, Compositor compositor, EventDispatcher dispatcher, GeckoBundle settings)493 private native void nativeTransfer(NativeQueue queue, Compositor compositor, 494 EventDispatcher dispatcher, GeckoBundle settings); 495 496 @WrapForJNI(dispatchTo = "proxy") attachEditable(IGeckoEditableParent parent, GeckoEditableChild child)497 public native void attachEditable(IGeckoEditableParent parent, 498 GeckoEditableChild child); 499 500 @WrapForJNI(calledFrom = "gecko") onReady(final @Nullable NativeQueue queue)501 private synchronized void onReady(final @Nullable NativeQueue queue) { 502 // onReady is called the first time the Gecko window is ready, with a null queue 503 // argument. In this case, we simply set the current queue to ready state. 504 // 505 // After the initial call, onReady is called again every time Window.transfer() 506 // is called, with a non-null queue argument. In this case, we only set the 507 // current queue to ready state _if_ the current queue matches the given queue, 508 // because if the queues don't match, we know there is another onReady call coming. 509 510 if ((queue == null && mNativeQueue == null) || 511 (queue != null && mNativeQueue != queue)) { 512 return; 513 } 514 515 if (mNativeQueue.checkAndSetState(State.INITIAL, State.READY) && 516 queue == null) { 517 Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() + 518 " - chrome startup finished"); 519 } 520 } 521 } 522 523 private class Listener implements BundleEventListener { registerListeners()524 /* package */ void registerListeners() { 525 getEventDispatcher().registerUiThreadListener(this, 526 "GeckoView:Prompt", 527 null); 528 } 529 530 @Override handleMessage(final String event, final GeckoBundle message, final EventCallback callback)531 public void handleMessage(final String event, final GeckoBundle message, 532 final EventCallback callback) { 533 if (DEBUG) { 534 Log.d(LOGTAG, "handleMessage: event = " + event); 535 } 536 537 if ("GeckoView:Prompt".equals(event)) { 538 handlePromptEvent(GeckoSession.this, message, callback); 539 } 540 } 541 } 542 543 protected Window mWindow; 544 private GeckoSessionSettings mSettings; 545 GeckoSession()546 public GeckoSession() { 547 this(null); 548 } 549 GeckoSession(final GeckoSessionSettings settings)550 public GeckoSession(final GeckoSessionSettings settings) { 551 mSettings = new GeckoSessionSettings(settings, this); 552 mListener.registerListeners(); 553 554 if (BuildConfig.DEBUG && handlersCount != mSessionHandlers.length) { 555 throw new AssertionError("Add new handler to handlers list"); 556 } 557 } 558 transferFrom(final Window window, final GeckoSessionSettings settings, final String id)559 private void transferFrom(final Window window, final GeckoSessionSettings settings, 560 final String id) { 561 if (isOpen()) { 562 throw new IllegalStateException("Session is open"); 563 } 564 565 if (window != null) { 566 onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ true); 567 } 568 569 mWindow = window; 570 mSettings = new GeckoSessionSettings(settings, this); 571 mId = id; 572 573 if (mWindow != null) { 574 mWindow.transfer(mNativeQueue, mCompositor, 575 mEventDispatcher, mSettings.asBundle()); 576 577 onWindowChanged(WINDOW_TRANSFER_IN, /* inProgress */ false); 578 } 579 } 580 transferFrom(final GeckoSession session)581 /* package */ void transferFrom(final GeckoSession session) { 582 final boolean changing = (session.mWindow != null); 583 if (changing) { 584 session.onWindowChanged(WINDOW_TRANSFER_OUT, /* inProgress */ true); 585 } 586 587 transferFrom(session.mWindow, session.mSettings, session.mId); 588 session.mWindow = null; 589 590 if (changing) { 591 session.onWindowChanged(WINDOW_TRANSFER_OUT, /* inProgress */ false); 592 } 593 } 594 595 @Override // Parcelable describeContents()596 public int describeContents() { 597 return 0; 598 } 599 600 @Override // Parcelable writeToParcel(Parcel out, int flags)601 public void writeToParcel(Parcel out, int flags) { 602 out.writeStrongInterface(mWindow); 603 out.writeParcelable(mSettings, flags); 604 out.writeString(mId); 605 } 606 607 // AIDL code may call readFromParcel even though it's not part of Parcelable. readFromParcel(final Parcel source)608 public void readFromParcel(final Parcel source) { 609 final IBinder binder = source.readStrongBinder(); 610 final IInterface ifce = (binder != null) ? 611 binder.queryLocalInterface(Window.class.getName()) : null; 612 final Window window = (ifce instanceof Window) ? (Window) ifce : null; 613 final GeckoSessionSettings settings = 614 source.readParcelable(getClass().getClassLoader()); 615 final String id = source.readString(); 616 transferFrom(window, settings, id); 617 } 618 619 public static final Creator<GeckoSession> CREATOR = new Creator<GeckoSession>() { 620 @Override 621 public GeckoSession createFromParcel(final Parcel in) { 622 final GeckoSession session = new GeckoSession(); 623 session.readFromParcel(in); 624 return session; 625 } 626 627 @Override 628 public GeckoSession[] newArray(final int size) { 629 return new GeckoSession[size]; 630 } 631 }; 632 633 /** 634 * Preload GeckoSession by starting Gecko in the background, if Gecko is not already running. 635 * 636 * @param context Activity or Application Context for starting GeckoSession. 637 */ preload(final @NonNull Context context)638 public static void preload(final @NonNull Context context) { 639 preload(context, /* geckoArgs */ null, 640 /* extras */ null, /* multiprocess */ false); 641 } 642 643 /** 644 * Preload GeckoSession by starting Gecko with the specified arguments in the background, 645 * if Gecko is not already running. 646 * 647 * @param context Activity or Application Context for starting GeckoSession. 648 * @param geckoArgs Arguments to be passed to Gecko, if Gecko is not already running. 649 * @param multiprocess True if child process in multiprocess mode should be preloaded. 650 */ preload(final @NonNull Context context, final @Nullable String[] geckoArgs, final @Nullable Bundle extras, final boolean multiprocess)651 public static void preload(final @NonNull Context context, 652 final @Nullable String[] geckoArgs, 653 final @Nullable Bundle extras, 654 final boolean multiprocess) { 655 final Context appContext = context.getApplicationContext(); 656 if (!appContext.equals(GeckoAppShell.getApplicationContext())) { 657 GeckoAppShell.setApplicationContext(appContext); 658 } 659 660 if (GeckoThread.isLaunched()) { 661 return; 662 } 663 664 final int flags = multiprocess ? GeckoThread.FLAG_PRELOAD_CHILD : 0; 665 if (GeckoThread.initMainProcess(/* profile */ null, geckoArgs, extras, flags)) { 666 GeckoThread.launch(); 667 } 668 } 669 isOpen()670 public boolean isOpen() { 671 return mWindow != null; 672 } 673 isReady()674 /* package */ boolean isReady() { 675 return mNativeQueue.isReady(); 676 } 677 678 /** 679 * Opens the session. 680 * 681 * The session is in a 'closed' state when first created. Opening it creates 682 * the underlying Gecko objects necessary to load a page, etc. Most GeckoSession 683 * methods only take affect on an open session, and are queued until the session 684 * is opened here. Opening a session is an asynchronous operation. You can check 685 * the current state via isOpen(). 686 * 687 * Call this when you are ready to use a GeckoSession instance. 688 * 689 * @param appContext An application context 690 */ open(final @Nullable Context appContext)691 public void open(final @Nullable Context appContext) { 692 ThreadUtils.assertOnUiThread(); 693 694 if (isOpen()) { 695 throw new IllegalStateException("Session is open"); 696 } 697 698 if (appContext != null) { 699 final boolean multiprocess = 700 mSettings.getBoolean(GeckoSessionSettings.USE_MULTIPROCESS); 701 preload(appContext, /* geckoArgs */ null, /* extras */ null, multiprocess); 702 } 703 704 openWindow(); 705 } 706 openWindow()707 private void openWindow() { 708 final String chromeUri = mSettings.getString(GeckoSessionSettings.CHROME_URI); 709 final int screenId = mSettings.getInt(GeckoSessionSettings.SCREEN_ID); 710 final boolean isPrivate = mSettings.getBoolean(GeckoSessionSettings.USE_PRIVATE_MODE); 711 712 mWindow = new Window(mNativeQueue); 713 714 onWindowChanged(WINDOW_OPEN, /* inProgress */ true); 715 716 if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) { 717 Window.open(mWindow, mNativeQueue, mCompositor, mEventDispatcher, 718 mSettings.asBundle(), mId, chromeUri, screenId, isPrivate); 719 } else { 720 GeckoThread.queueNativeCallUntil( 721 GeckoThread.State.PROFILE_READY, 722 Window.class, "open", 723 Window.class, mWindow, 724 NativeQueue.class, mNativeQueue, 725 Compositor.class, mCompositor, 726 EventDispatcher.class, mEventDispatcher, 727 GeckoBundle.class, mSettings.asBundle(), 728 String.class, mId, 729 String.class, chromeUri, 730 screenId, isPrivate); 731 } 732 733 onWindowChanged(WINDOW_OPEN, /* inProgress */ false); 734 } 735 736 /** 737 * Closes the session. 738 * 739 * This frees the underlying Gecko objects and unloads the current page. The session may be 740 * reopened later, but page state is not restored. Call this when you are finished using 741 * a GeckoSession instance. 742 */ close()743 public void close() { 744 ThreadUtils.assertOnUiThread(); 745 746 if (!isOpen()) { 747 Log.w(LOGTAG, "Attempted to close a GeckoSession that was already closed."); 748 return; 749 } 750 751 onWindowChanged(WINDOW_CLOSE, /* inProgress */ true); 752 753 mWindow.close(); 754 mWindow.disposeNative(); 755 mWindow = null; 756 757 onWindowChanged(WINDOW_CLOSE, /* inProgress */ false); 758 } 759 onWindowChanged(int change, boolean inProgress)760 private void onWindowChanged(int change, boolean inProgress) { 761 if ((change == WINDOW_OPEN || change == WINDOW_TRANSFER_IN) && !inProgress) { 762 mTextInput.onWindowChanged(mWindow); 763 } 764 765 if (change == WINDOW_CLOSE) { 766 // Detach when window is closing, and reattach immediately after window is closed. 767 // We reattach immediate after closing because we want any actions performed while the 768 // session is closed to be properly queued, until the session is open again. 769 for (final GeckoSessionHandler<?> handler : mSessionHandlers) { 770 handler.setSessionIsReady(getEventDispatcher(), !inProgress); 771 } 772 } 773 } 774 775 /** 776 * Get the TextInputController instance for this session. 777 * 778 * @return TextInputController instance. 779 */ getTextInputController()780 public @NonNull TextInputController getTextInputController() { 781 // May be called on any thread. 782 return mTextInput; 783 } 784 785 /** 786 * Load the given URI. 787 * @param uri The URI of the resource to load. 788 */ loadUri(String uri)789 public void loadUri(String uri) { 790 final GeckoBundle msg = new GeckoBundle(); 791 msg.putString("uri", uri); 792 mEventDispatcher.dispatch("GeckoView:LoadUri", msg); 793 } 794 795 /** 796 * Load the given URI. 797 * @param uri The URI of the resource to load. 798 */ loadUri(Uri uri)799 public void loadUri(Uri uri) { 800 loadUri(uri.toString()); 801 } 802 803 /** 804 * Reload the current URI. 805 */ reload()806 public void reload() { 807 mEventDispatcher.dispatch("GeckoView:Reload", null); 808 } 809 810 /** 811 * Stop loading. 812 */ stop()813 public void stop() { 814 mEventDispatcher.dispatch("GeckoView:Stop", null); 815 } 816 817 /** 818 * Go back in history. 819 */ goBack()820 public void goBack() { 821 mEventDispatcher.dispatch("GeckoView:GoBack", null); 822 } 823 824 /** 825 * Go forward in history. 826 */ goForward()827 public void goForward() { 828 mEventDispatcher.dispatch("GeckoView:GoForward", null); 829 } 830 831 /** 832 * Set this GeckoSession as active or inactive. Setting a GeckoSession to inactive will 833 * significantly reduce its memory footprint, but should only be done if the 834 * GeckoSession is not currently visible. 835 * @param active A boolean determining whether the GeckoSession is active 836 */ setActive(boolean active)837 public void setActive(boolean active) { 838 final GeckoBundle msg = new GeckoBundle(); 839 msg.putBoolean("active", active); 840 mEventDispatcher.dispatch("GeckoView:SetActive", msg); 841 } 842 getSettings()843 public GeckoSessionSettings getSettings() { 844 return mSettings; 845 } 846 importScript(final String url)847 public void importScript(final String url) { 848 if (url.startsWith("resource://android/assets/")) { 849 final GeckoBundle data = new GeckoBundle(1); 850 data.putString("scriptURL", url); 851 getEventDispatcher().dispatch("GeckoView:ImportScript", data); 852 return; 853 } 854 855 throw new IllegalArgumentException("Must import script from 'resources://android/assets/' location."); 856 } 857 858 /** 859 * Exits fullscreen mode 860 */ exitFullScreen()861 public void exitFullScreen() { 862 mEventDispatcher.dispatch("GeckoViewContent:ExitFullScreen", null); 863 } 864 865 /** 866 * Set the content callback handler. 867 * This will replace the current handler. 868 * @param delegate An implementation of ContentDelegate. 869 */ setContentDelegate(ContentDelegate delegate)870 public void setContentDelegate(ContentDelegate delegate) { 871 mContentHandler.setDelegate(delegate, this); 872 } 873 874 /** 875 * Get the content callback handler. 876 * @return The current content callback handler. 877 */ getContentDelegate()878 public ContentDelegate getContentDelegate() { 879 return mContentHandler.getDelegate(); 880 } 881 882 /** 883 * Set the progress callback handler. 884 * This will replace the current handler. 885 * @param delegate An implementation of ProgressDelegate. 886 */ setProgressDelegate(ProgressDelegate delegate)887 public void setProgressDelegate(ProgressDelegate delegate) { 888 mProgressHandler.setDelegate(delegate, this); 889 } 890 891 /** 892 * Get the progress callback handler. 893 * @return The current progress callback handler. 894 */ getProgressDelegate()895 public ProgressDelegate getProgressDelegate() { 896 return mProgressHandler.getDelegate(); 897 } 898 899 /** 900 * Set the navigation callback handler. 901 * This will replace the current handler. 902 * @param delegate An implementation of NavigationDelegate. 903 */ setNavigationDelegate(NavigationDelegate delegate)904 public void setNavigationDelegate(NavigationDelegate delegate) { 905 mNavigationHandler.setDelegate(delegate, this); 906 } 907 908 /** 909 * Get the navigation callback handler. 910 * @return The current navigation callback handler. 911 */ getNavigationDelegate()912 public NavigationDelegate getNavigationDelegate() { 913 return mNavigationHandler.getDelegate(); 914 } 915 916 /** 917 * Set the content scroll callback handler. 918 * This will replace the current handler. 919 * @param delegate An implementation of ScrollDelegate. 920 */ setScrollDelegate(ScrollDelegate delegate)921 public void setScrollDelegate(ScrollDelegate delegate) { 922 mScrollHandler.setDelegate(delegate, this); 923 } 924 getScrollDelegate()925 public ScrollDelegate getScrollDelegate() { 926 return mScrollHandler.getDelegate(); 927 } 928 929 /** 930 * Set the tracking protection callback handler. 931 * This will replace the current handler. 932 * @param delegate An implementation of TrackingProtectionDelegate. 933 */ setTrackingProtectionDelegate(TrackingProtectionDelegate delegate)934 public void setTrackingProtectionDelegate(TrackingProtectionDelegate delegate) { 935 mTrackingProtectionHandler.setDelegate(delegate, this); 936 } 937 938 /** 939 * Get the tracking protection callback handler. 940 * @return The current tracking protection callback handler. 941 */ getTrackingProtectionDelegate()942 public TrackingProtectionDelegate getTrackingProtectionDelegate() { 943 return mTrackingProtectionHandler.getDelegate(); 944 } 945 946 /** 947 * Set the current prompt delegate for this GeckoSession. 948 * @param delegate PromptDelegate instance or null to use the built-in delegate. 949 */ setPromptDelegate(PromptDelegate delegate)950 public void setPromptDelegate(PromptDelegate delegate) { 951 mPromptDelegate = delegate; 952 } 953 954 /** 955 * Get the current prompt delegate for this GeckoSession. 956 * @return PromptDelegate instance or null if using built-in delegate. 957 */ getPromptDelegate()958 public PromptDelegate getPromptDelegate() { 959 return mPromptDelegate; 960 } 961 962 private static class PromptCallback implements 963 PromptDelegate.AlertCallback, PromptDelegate.ButtonCallback, 964 PromptDelegate.TextCallback, PromptDelegate.AuthCallback, 965 PromptDelegate.ChoiceCallback, PromptDelegate.FileCallback { 966 967 private final String mType; 968 private final String mMode; 969 private final boolean mHasCheckbox; 970 private final String mCheckboxMessage; 971 972 private EventCallback mCallback; 973 private boolean mCheckboxValue; 974 private GeckoBundle mResult; 975 PromptCallback(final String type, final String mode, final GeckoBundle message, final EventCallback callback)976 public PromptCallback(final String type, final String mode, 977 final GeckoBundle message, final EventCallback callback) { 978 mType = type; 979 mMode = mode; 980 mCallback = callback; 981 mHasCheckbox = message.getBoolean("hasCheck"); 982 mCheckboxMessage = message.getString("checkMsg"); 983 mCheckboxValue = message.getBoolean("checkValue"); 984 } 985 ensureResult()986 private GeckoBundle ensureResult() { 987 if (mResult == null) { 988 // Usually result object contains two items. 989 mResult = new GeckoBundle(2); 990 } 991 return mResult; 992 } 993 submit()994 private void submit() { 995 if (mHasCheckbox) { 996 ensureResult().putBoolean("checkValue", mCheckboxValue); 997 } 998 if (mCallback != null) { 999 mCallback.sendSuccess(mResult); 1000 mCallback = null; 1001 } 1002 } 1003 1004 @Override // AlertCallbcak dismiss()1005 public void dismiss() { 1006 // Send a null result. 1007 mResult = null; 1008 submit(); 1009 } 1010 1011 @Override // AlertCallbcak hasCheckbox()1012 public boolean hasCheckbox() { 1013 return mHasCheckbox; 1014 } 1015 1016 @Override // AlertCallbcak getCheckboxMessage()1017 public String getCheckboxMessage() { 1018 return mCheckboxMessage; 1019 } 1020 1021 @Override // AlertCallbcak getCheckboxValue()1022 public boolean getCheckboxValue() { 1023 return mCheckboxValue; 1024 } 1025 1026 @Override // AlertCallbcak setCheckboxValue(final boolean value)1027 public void setCheckboxValue(final boolean value) { 1028 mCheckboxValue = value; 1029 } 1030 1031 @Override // ButtonCallback confirm(final int value)1032 public void confirm(final int value) { 1033 if ("button".equals(mType)) { 1034 ensureResult().putInt("button", value); 1035 } else { 1036 throw new UnsupportedOperationException(); 1037 } 1038 submit(); 1039 } 1040 1041 @Override // TextCallback, AuthCallback, ChoiceCallback, FileCallback confirm(final String value)1042 public void confirm(final String value) { 1043 if ("text".equals(mType) || "color".equals(mType) || "datetime".equals(mType)) { 1044 ensureResult().putString(mType, value); 1045 } else if ("auth".equals(mType)) { 1046 if (!"password".equals(mMode)) { 1047 throw new IllegalArgumentException(); 1048 } 1049 ensureResult().putString("password", value); 1050 } else if ("choice".equals(mType)) { 1051 confirm(new String[] { value }); 1052 return; 1053 } else { 1054 throw new UnsupportedOperationException(); 1055 } 1056 submit(); 1057 } 1058 1059 @Override // AuthCallback confirm(final String username, final String password)1060 public void confirm(final String username, final String password) { 1061 if ("auth".equals(mType)) { 1062 if (!"auth".equals(mMode)) { 1063 throw new IllegalArgumentException(); 1064 } 1065 ensureResult().putString("username", username); 1066 ensureResult().putString("password", password); 1067 } else { 1068 throw new UnsupportedOperationException(); 1069 } 1070 submit(); 1071 } 1072 1073 @Override // ChoiceCallback, FileCallback confirm(final String[] values)1074 public void confirm(final String[] values) { 1075 if (("menu".equals(mMode) || "single".equals(mMode)) && 1076 (values == null || values.length != 1)) { 1077 throw new IllegalArgumentException(); 1078 } 1079 if ("choice".equals(mType)) { 1080 ensureResult().putStringArray("choices", values); 1081 } else { 1082 throw new UnsupportedOperationException(); 1083 } 1084 submit(); 1085 } 1086 1087 @Override // ChoiceCallback confirm(PromptDelegate.Choice item)1088 public void confirm(PromptDelegate.Choice item) { 1089 if ("choice".equals(mType)) { 1090 confirm(item == null ? null : item.id); 1091 return; 1092 } else { 1093 throw new UnsupportedOperationException(); 1094 } 1095 } 1096 1097 @Override // ChoiceCallback confirm(PromptDelegate.Choice[] items)1098 public void confirm(PromptDelegate.Choice[] items) { 1099 if (("menu".equals(mMode) || "single".equals(mMode)) && 1100 (items == null || items.length != 1)) { 1101 throw new IllegalArgumentException(); 1102 } 1103 if ("choice".equals(mType)) { 1104 if (items == null) { 1105 confirm((String[]) null); 1106 return; 1107 } 1108 final String[] ids = new String[items.length]; 1109 for (int i = 0; i < ids.length; i++) { 1110 ids[i] = (items[i] == null) ? null : items[i].id; 1111 } 1112 confirm(ids); 1113 return; 1114 } else { 1115 throw new UnsupportedOperationException(); 1116 } 1117 } 1118 1119 @Override // FileCallback confirm(final Context context, final Uri uri)1120 public void confirm(final Context context, final Uri uri) { 1121 if ("file".equals(mType)) { 1122 confirm(context, uri == null ? null : new Uri[] { uri }); 1123 return; 1124 } else { 1125 throw new UnsupportedOperationException(); 1126 } 1127 } 1128 getFile(final Context context, final Uri uri)1129 private static String getFile(final Context context, final Uri uri) { 1130 if (uri == null) { 1131 return null; 1132 } 1133 if ("file".equals(uri.getScheme())) { 1134 return uri.getPath(); 1135 } 1136 final ContentResolver cr = context.getContentResolver(); 1137 final Cursor cur = cr.query(uri, new String[] { "_data" }, /* selection */ null, 1138 /* args */ null, /* sort */ null); 1139 if (cur == null) { 1140 return null; 1141 } 1142 try { 1143 final int idx = cur.getColumnIndex("_data"); 1144 if (idx < 0 || !cur.moveToFirst()) { 1145 return null; 1146 } 1147 do { 1148 try { 1149 final String path = cur.getString(idx); 1150 if (path != null && !path.isEmpty()) { 1151 return path; 1152 } 1153 } catch (final Exception e) { 1154 } 1155 } while (cur.moveToNext()); 1156 } finally { 1157 cur.close(); 1158 } 1159 return null; 1160 } 1161 1162 @Override // FileCallback confirm(final Context context, final Uri[] uris)1163 public void confirm(final Context context, final Uri[] uris) { 1164 if ("single".equals(mMode) && (uris == null || uris.length != 1)) { 1165 throw new IllegalArgumentException(); 1166 } 1167 if ("file".equals(mType)) { 1168 final String[] paths = new String[uris != null ? uris.length : 0]; 1169 for (int i = 0; i < paths.length; i++) { 1170 paths[i] = getFile(context, uris[i]); 1171 if (paths[i] == null) { 1172 Log.e(LOGTAG, "Only file URI is supported: " + uris[i]); 1173 } 1174 } 1175 ensureResult().putStringArray("files", paths); 1176 } else { 1177 throw new UnsupportedOperationException(); 1178 } 1179 submit(); 1180 } 1181 } 1182 handlePromptEvent(final GeckoSession session, final GeckoBundle message, final EventCallback callback)1183 /* package */ static void handlePromptEvent(final GeckoSession session, 1184 final GeckoBundle message, 1185 final EventCallback callback) { 1186 final PromptDelegate delegate = session.getPromptDelegate(); 1187 if (delegate == null) { 1188 // Default behavior is same as calling dismiss() on callback. 1189 callback.sendSuccess(null); 1190 return; 1191 } 1192 1193 final String type = message.getString("type"); 1194 final String mode = message.getString("mode"); 1195 final PromptCallback cb = new PromptCallback(type, mode, message, callback); 1196 final String title = message.getString("title"); 1197 final String msg = message.getString("msg"); 1198 switch (type) { 1199 case "alert": { 1200 delegate.onAlert(session, title, msg, cb); 1201 break; 1202 } 1203 case "button": { 1204 final String[] btnTitle = message.getStringArray("btnTitle"); 1205 final String[] btnCustomTitle = message.getStringArray("btnCustomTitle"); 1206 for (int i = 0; i < btnCustomTitle.length; i++) { 1207 final int resId; 1208 if ("ok".equals(btnTitle[i])) { 1209 resId = android.R.string.ok; 1210 } else if ("cancel".equals(btnTitle[i])) { 1211 resId = android.R.string.cancel; 1212 } else if ("yes".equals(btnTitle[i])) { 1213 resId = android.R.string.yes; 1214 } else if ("no".equals(btnTitle[i])) { 1215 resId = android.R.string.no; 1216 } else { 1217 continue; 1218 } 1219 btnCustomTitle[i] = Resources.getSystem().getString(resId); 1220 } 1221 delegate.onButtonPrompt(session, title, msg, btnCustomTitle, cb); 1222 break; 1223 } 1224 case "text": { 1225 delegate.onTextPrompt(session, title, msg, message.getString("value"), cb); 1226 break; 1227 } 1228 case "auth": { 1229 delegate.onAuthPrompt(session, title, msg, new PromptDelegate.AuthOptions(message.getBundle("options")), cb); 1230 break; 1231 } 1232 case "choice": { 1233 final int intMode; 1234 if ("menu".equals(mode)) { 1235 intMode = PromptDelegate.Choice.CHOICE_TYPE_MENU; 1236 } else if ("single".equals(mode)) { 1237 intMode = PromptDelegate.Choice.CHOICE_TYPE_SINGLE; 1238 } else if ("multiple".equals(mode)) { 1239 intMode = PromptDelegate.Choice.CHOICE_TYPE_MULTIPLE; 1240 } else { 1241 callback.sendError("Invalid mode"); 1242 return; 1243 } 1244 1245 GeckoBundle[] choiceBundles = message.getBundleArray("choices"); 1246 PromptDelegate.Choice choices[]; 1247 if (choiceBundles == null || choiceBundles.length == 0) { 1248 choices = null; 1249 } else { 1250 choices = new PromptDelegate.Choice[choiceBundles.length]; 1251 for (int i = 0; i < choiceBundles.length; i++) { 1252 choices[i] = new PromptDelegate.Choice(choiceBundles[i]); 1253 } 1254 } 1255 delegate.onChoicePrompt(session, title, msg, intMode, 1256 choices, cb); 1257 break; 1258 } 1259 case "color": { 1260 delegate.onColorPrompt(session, title, message.getString("value"), cb); 1261 break; 1262 } 1263 case "datetime": { 1264 final int intMode; 1265 if ("date".equals(mode)) { 1266 intMode = PromptDelegate.DATETIME_TYPE_DATE; 1267 } else if ("month".equals(mode)) { 1268 intMode = PromptDelegate.DATETIME_TYPE_MONTH; 1269 } else if ("week".equals(mode)) { 1270 intMode = PromptDelegate.DATETIME_TYPE_WEEK; 1271 } else if ("time".equals(mode)) { 1272 intMode = PromptDelegate.DATETIME_TYPE_TIME; 1273 } else if ("datetime-local".equals(mode)) { 1274 intMode = PromptDelegate.DATETIME_TYPE_DATETIME_LOCAL; 1275 } else { 1276 callback.sendError("Invalid mode"); 1277 return; 1278 } 1279 delegate.onDateTimePrompt(session, title, intMode, 1280 message.getString("value"), 1281 message.getString("min"), 1282 message.getString("max"), cb); 1283 break; 1284 } 1285 case "file": { 1286 final int intMode; 1287 if ("single".equals(mode)) { 1288 intMode = PromptDelegate.FILE_TYPE_SINGLE; 1289 } else if ("multiple".equals(mode)) { 1290 intMode = PromptDelegate.FILE_TYPE_MULTIPLE; 1291 } else { 1292 callback.sendError("Invalid mode"); 1293 return; 1294 } 1295 String[] mimeTypes = message.getStringArray("mimeTypes"); 1296 final String[] extensions = message.getStringArray("extension"); 1297 if (extensions != null) { 1298 final ArrayList<String> combined = 1299 new ArrayList<>(mimeTypes.length + extensions.length); 1300 combined.addAll(Arrays.asList(mimeTypes)); 1301 for (final String extension : extensions) { 1302 final String mimeType = 1303 URLConnection.guessContentTypeFromName(extension); 1304 if (mimeType != null) { 1305 combined.add(mimeType); 1306 } 1307 } 1308 mimeTypes = combined.toArray(new String[combined.size()]); 1309 } 1310 delegate.onFilePrompt(session, title, intMode, mimeTypes, cb); 1311 break; 1312 } 1313 default: { 1314 callback.sendError("Invalid type"); 1315 break; 1316 } 1317 } 1318 } 1319 getEventDispatcher()1320 public EventDispatcher getEventDispatcher() { 1321 return mEventDispatcher; 1322 } 1323 1324 public interface ProgressDelegate { 1325 /** 1326 * Class representing security information for a site. 1327 */ 1328 public class SecurityInformation { 1329 public static final int SECURITY_MODE_UNKNOWN = 0; 1330 public static final int SECURITY_MODE_IDENTIFIED = 1; 1331 public static final int SECURITY_MODE_VERIFIED = 2; 1332 1333 public static final int CONTENT_UNKNOWN = 0; 1334 public static final int CONTENT_BLOCKED = 1; 1335 public static final int CONTENT_LOADED = 2; 1336 /** 1337 * Indicates whether or not the site is secure. 1338 */ 1339 public final boolean isSecure; 1340 /** 1341 * Indicates whether or not the site is a security exception. 1342 */ 1343 public final boolean isException; 1344 /** 1345 * Contains the origin of the certificate. 1346 */ 1347 public final String origin; 1348 /** 1349 * Contains the host associated with the certificate. 1350 */ 1351 public final String host; 1352 /** 1353 * Contains the human-readable name of the certificate subject. 1354 */ 1355 public final String organization; 1356 /** 1357 * Contains the full name of the certificate subject, including location. 1358 */ 1359 public final String subjectName; 1360 /** 1361 * Contains the common name of the issuing authority. 1362 */ 1363 public final String issuerCommonName; 1364 /** 1365 * Contains the full/proper name of the issuing authority. 1366 */ 1367 public final String issuerOrganization; 1368 /** 1369 * Indicates the security level of the site; possible values are SECURITY_MODE_UNKNOWN, 1370 * SECURITY_MODE_IDENTIFIED, and SECURITY_MODE_VERIFIED. SECURITY_MODE_IDENTIFIED 1371 * indicates domain validation only, while SECURITY_MODE_VERIFIED indicates extended validation. 1372 */ 1373 public final int securityMode; 1374 /** 1375 * Indicates the presence of passive mixed content; possible values are 1376 * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED. 1377 */ 1378 public final int mixedModePassive; 1379 /** 1380 * Indicates the presence of active mixed content; possible values are 1381 * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED. 1382 */ 1383 public final int mixedModeActive; 1384 /** 1385 * Indicates the status of tracking protection; possible values are 1386 * CONTENT_UNKNOWN, CONTENT_BLOCKED, and CONTENT_LOADED. 1387 */ 1388 public final int trackingMode; 1389 SecurityInformation(GeckoBundle identityData)1390 /* package */ SecurityInformation(GeckoBundle identityData) { 1391 final GeckoBundle mode = identityData.getBundle("mode"); 1392 1393 mixedModePassive = mode.getInt("mixed_display"); 1394 mixedModeActive = mode.getInt("mixed_active"); 1395 trackingMode = mode.getInt("tracking"); 1396 1397 securityMode = mode.getInt("identity"); 1398 1399 isSecure = identityData.getBoolean("secure"); 1400 isException = identityData.getBoolean("securityException"); 1401 origin = identityData.getString("origin"); 1402 host = identityData.getString("host"); 1403 organization = identityData.getString("organization"); 1404 subjectName = identityData.getString("subjectName"); 1405 issuerCommonName = identityData.getString("issuerCommonName"); 1406 issuerOrganization = identityData.getString("issuerOrganization"); 1407 } 1408 } 1409 1410 /** 1411 * A View has started loading content from the network. 1412 * @param session GeckoSession that initiated the callback. 1413 * @param url The resource being loaded. 1414 */ onPageStart(GeckoSession session, String url)1415 void onPageStart(GeckoSession session, String url); 1416 1417 /** 1418 * A View has finished loading content from the network. 1419 * @param session GeckoSession that initiated the callback. 1420 * @param success Whether the page loaded successfully or an error occurred. 1421 */ onPageStop(GeckoSession session, boolean success)1422 void onPageStop(GeckoSession session, boolean success); 1423 1424 /** 1425 * The security status has been updated. 1426 * @param session GeckoSession that initiated the callback. 1427 * @param securityInfo The new security information. 1428 */ onSecurityChange(GeckoSession session, SecurityInformation securityInfo)1429 void onSecurityChange(GeckoSession session, SecurityInformation securityInfo); 1430 } 1431 1432 public interface ContentDelegate { 1433 /** 1434 * A page title was discovered in the content or updated after the content 1435 * loaded. 1436 * @param session The GeckoSession that initiated the callback. 1437 * @param title The title sent from the content. 1438 */ onTitleChange(GeckoSession session, String title)1439 void onTitleChange(GeckoSession session, String title); 1440 1441 /** 1442 * A page has requested focus. Note that window.focus() in content will not result 1443 * in this being called. 1444 * @param session The GeckoSession that initiated the callback. 1445 */ onFocusRequest(GeckoSession session)1446 void onFocusRequest(GeckoSession session); 1447 1448 /** 1449 * A page has requested to close 1450 * @param session The GeckoSession that initiated the callback. 1451 */ onCloseRequest(GeckoSession session)1452 void onCloseRequest(GeckoSession session); 1453 1454 /** 1455 * A page has entered or exited full screen mode. Typically, the implementation 1456 * would set the Activity containing the GeckoSession to full screen when the page is 1457 * in full screen mode. 1458 * 1459 * @param session The GeckoSession that initiated the callback. 1460 * @param fullScreen True if the page is in full screen mode. 1461 */ onFullScreen(GeckoSession session, boolean fullScreen)1462 void onFullScreen(GeckoSession session, boolean fullScreen); 1463 1464 1465 /** 1466 * A user has initiated the context menu via long-press. 1467 * This event is fired on links, (nested) images and (nested) media 1468 * elements. 1469 * 1470 * @param session The GeckoSession that initiated the callback. 1471 * @param screenX The screen coordinates of the press. 1472 * @param screenY The screen coordinates of the press. 1473 * @param uri The URI of the pressed link, set for links and 1474 * image-links. 1475 * @param elementSrc The source URI of the pressed element, set for 1476 * (nested) images and media elements. 1477 */ onContextMenu(GeckoSession session, int screenX, int screenY, String uri, String elementSrc)1478 void onContextMenu(GeckoSession session, int screenX, int screenY, 1479 String uri, String elementSrc); 1480 } 1481 1482 /** 1483 * This is used to send responses in delegate methods that have asynchronous responses. 1484 */ 1485 public interface Response<T> { 1486 /** 1487 * @param val The value contained in the response 1488 */ respond(T val)1489 void respond(T val); 1490 } 1491 1492 public interface NavigationDelegate { 1493 /** 1494 * A view has started loading content from the network. 1495 * @param session The GeckoSession that initiated the callback. 1496 * @param url The resource being loaded. 1497 */ onLocationChange(GeckoSession session, String url)1498 void onLocationChange(GeckoSession session, String url); 1499 1500 /** 1501 * The view's ability to go back has changed. 1502 * @param session The GeckoSession that initiated the callback. 1503 * @param canGoBack The new value for the ability. 1504 */ onCanGoBack(GeckoSession session, boolean canGoBack)1505 void onCanGoBack(GeckoSession session, boolean canGoBack); 1506 1507 /** 1508 * The view's ability to go forward has changed. 1509 * @param session The GeckoSession that initiated the callback. 1510 * @param canGoForward The new value for the ability. 1511 */ onCanGoForward(GeckoSession session, boolean canGoForward)1512 void onCanGoForward(GeckoSession session, boolean canGoForward); 1513 1514 public static final int TARGET_WINDOW_NONE = 0; 1515 public static final int TARGET_WINDOW_CURRENT = 1; 1516 public static final int TARGET_WINDOW_NEW = 2; 1517 1518 /** 1519 * A request to open an URI. 1520 * @param session The GeckoSession that initiated the callback. 1521 * @param uri The URI to be loaded. 1522 * @param target The target where the window has requested to open. One of 1523 * TARGET_WINDOW_*. 1524 * 1525 * @return Whether or not the load was handled. Returning false will allow Gecko 1526 * to continue the load as normal. 1527 */ onLoadRequest(GeckoSession session, String uri, int target)1528 boolean onLoadRequest(GeckoSession session, String uri, int target); 1529 1530 /** 1531 * A request has been made to open a new session. The URI is provided only for 1532 * informational purposes. Do not call GeckoSession.loadUri() here. Additionally, the 1533 * returned GeckoSession must be a newly-created one. 1534 * 1535 * @param session The GeckoSession that initiated the callback. 1536 * @param uri The URI to be loaded. 1537 * 1538 * @param response A Response which will hold the returned GeckoSession 1539 */ onNewSession(GeckoSession session, String uri, Response<GeckoSession> response)1540 void onNewSession(GeckoSession session, String uri, Response<GeckoSession> response); 1541 } 1542 1543 /** 1544 * GeckoSession applications implement this interface to handle prompts triggered by 1545 * content in the GeckoSession, such as alerts, authentication dialogs, and select list 1546 * pickers. 1547 **/ 1548 public interface PromptDelegate { 1549 /** 1550 * Callback interface for notifying the result of a prompt, and for accessing the 1551 * optional features for prompts (e.g. optional checkbox). 1552 */ 1553 interface AlertCallback { 1554 /** 1555 * Called by the prompt implementation when the prompt is dismissed without a 1556 * result, for example if the user presses the "Back" button. All prompts 1557 * must call dismiss() or confirm(), if available, when the prompt is dismissed. 1558 */ dismiss()1559 void dismiss(); 1560 1561 /** 1562 * Return whether the prompt shown should include a checkbox. For example, if 1563 * a page shows multiple prompts within a short period of time, the next 1564 * prompt will include a checkbox to let the user disable future prompts. 1565 * Although the API allows checkboxes for all prompts, in practice, only 1566 * alert/button/text/auth prompts will possibly have a checkbox. 1567 * 1568 * @return True if prompt includes a checkbox. 1569 */ hasCheckbox()1570 boolean hasCheckbox(); 1571 1572 /** 1573 * Return the message label for the optional checkbox. 1574 * 1575 * @return Checkbox message or null if none. 1576 */ getCheckboxMessage()1577 String getCheckboxMessage(); 1578 1579 /** 1580 * Return the initial value for the optional checkbox. 1581 * 1582 * @return Initial checkbox value. 1583 */ getCheckboxValue()1584 boolean getCheckboxValue(); 1585 1586 /** 1587 * Set the current value for the optional checkbox. 1588 * 1589 * @param value New checkbox value. 1590 */ setCheckboxValue(boolean value)1591 void setCheckboxValue(boolean value); 1592 } 1593 1594 /** 1595 * Display a simple message prompt. 1596 * 1597 * @param session GeckoSession that triggered the prompt 1598 * @param title Title for the prompt dialog. 1599 * @param msg Message for the prompt dialog. 1600 * @param callback Callback interface. 1601 */ onAlert(GeckoSession session, String title, String msg, AlertCallback callback)1602 void onAlert(GeckoSession session, String title, String msg, AlertCallback callback); 1603 1604 /** 1605 * Callback interface for notifying the result of a button prompt. 1606 */ 1607 interface ButtonCallback extends AlertCallback { 1608 /** 1609 * Called by the prompt implementation when the button prompt is dismissed by 1610 * the user pressing one of the buttons. 1611 * 1612 * @param button Button result; one of BUTTON_TYPE_* constants. 1613 */ confirm(int button)1614 void confirm(int button); 1615 } 1616 1617 static final int BUTTON_TYPE_POSITIVE = 0; 1618 static final int BUTTON_TYPE_NEUTRAL = 1; 1619 static final int BUTTON_TYPE_NEGATIVE = 2; 1620 1621 /** 1622 * Display a prompt with up to three buttons. 1623 * 1624 * @param session GeckoSession that triggered the prompt 1625 * @param title Title for the prompt dialog. 1626 * @param msg Message for the prompt dialog. 1627 * @param btnMsg Array of 3 elements indicating labels for the individual buttons. 1628 * btnMsg[BUTTON_TYPE_POSITIVE] is the label for the "positive" button. 1629 * btnMsg[BUTTON_TYPE_NEUTRAL] is the label for the "neutral" button. 1630 * btnMsg[BUTTON_TYPE_NEGATIVE] is the label for the "negative" button. 1631 * The button is hidden if the corresponding label is null. 1632 * @param callback Callback interface. 1633 */ onButtonPrompt(GeckoSession session, String title, String msg, String[] btnMsg, ButtonCallback callback)1634 void onButtonPrompt(GeckoSession session, String title, String msg, 1635 String[] btnMsg, ButtonCallback callback); 1636 1637 /** 1638 * Callback interface for notifying the result of prompts that have text results, 1639 * including color and date/time pickers. 1640 */ 1641 interface TextCallback extends AlertCallback { 1642 /** 1643 * Called by the prompt implementation when the text prompt is confirmed by 1644 * the user, for example by pressing the "OK" button. 1645 * 1646 * @param text Text result. 1647 */ confirm(String text)1648 void confirm(String text); 1649 } 1650 1651 /** 1652 * Display a prompt for inputting text. 1653 * 1654 * @param session GeckoSession that triggered the prompt 1655 * @param title Title for the prompt dialog. 1656 * @param msg Message for the prompt dialog. 1657 * @param value Default input text for the prompt. 1658 * @param callback Callback interface. 1659 */ onTextPrompt(GeckoSession session, String title, String msg, String value, TextCallback callback)1660 void onTextPrompt(GeckoSession session, String title, String msg, 1661 String value, TextCallback callback); 1662 1663 /** 1664 * Callback interface for notifying the result of authentication prompts. 1665 */ 1666 interface AuthCallback extends AlertCallback { 1667 /** 1668 * Called by the prompt implementation when a password-only prompt is 1669 * confirmed by the user. 1670 * 1671 * @param password Entered password. 1672 */ confirm(String password)1673 void confirm(String password); 1674 1675 /** 1676 * Called by the prompt implementation when a username/password prompt is 1677 * confirmed by the user. 1678 * 1679 * @param username Entered username. 1680 * @param password Entered password. 1681 */ confirm(String username, String password)1682 void confirm(String username, String password); 1683 } 1684 1685 class AuthOptions { 1686 /** 1687 * The auth prompt is for a network host. 1688 */ 1689 public static final int AUTH_FLAG_HOST = 1; 1690 /** 1691 * The auth prompt is for a proxy. 1692 */ 1693 public static final int AUTH_FLAG_PROXY = 2; 1694 /** 1695 * The auth prompt should only request a password. 1696 */ 1697 public static final int AUTH_FLAG_ONLY_PASSWORD = 8; 1698 /** 1699 * The auth prompt is the result of a previous failed login. 1700 */ 1701 public static final int AUTH_FLAG_PREVIOUS_FAILED = 16; 1702 /** 1703 * The auth prompt is for a cross-origin sub-resource. 1704 */ 1705 public static final int AUTH_FLAG_CROSS_ORIGIN_SUB_RESOURCE = 32; 1706 1707 /** 1708 * The auth request is unencrypted or the encryption status is unknown. 1709 */ 1710 public static final int AUTH_LEVEL_NONE = 0; 1711 /** 1712 * The auth request only encrypts password but not data. 1713 */ 1714 public static final int AUTH_LEVEL_PW_ENCRYPTED = 1; 1715 /** 1716 * The auth request encrypts both password and data. 1717 */ 1718 public static final int AUTH_LEVEL_SECURE = 2; 1719 1720 /** 1721 * An int bit-field of AUTH_FLAG_* flags. 1722 */ 1723 public int flags; 1724 1725 /** 1726 * A string containing the URI for the auth request or null if unknown. 1727 */ 1728 public String uri; 1729 1730 /** 1731 * An int, one of AUTH_LEVEL_*, indicating level of encryption. 1732 */ 1733 public int level; 1734 1735 /** 1736 * A string containing the initial username or null if password-only. 1737 */ 1738 public String username; 1739 1740 /** 1741 * A string containing the initial password. 1742 */ 1743 public String password; 1744 AuthOptions(GeckoBundle options)1745 /* package */ AuthOptions(GeckoBundle options) { 1746 flags = options.getInt("flags"); 1747 uri = options.getString("uri"); 1748 level = options.getInt("level"); 1749 username = options.getString("username"); 1750 password = options.getString("password"); 1751 } 1752 } 1753 1754 /** 1755 * Display a prompt for authentication credentials. 1756 * 1757 * @param session GeckoSession that triggered the prompt 1758 * @param title Title for the prompt dialog. 1759 * @param msg Message for the prompt dialog. 1760 * @param options AuthOptions containing options for the prompt 1761 * @param callback Callback interface. 1762 */ onAuthPrompt(GeckoSession session, String title, String msg, AuthOptions options, AuthCallback callback)1763 void onAuthPrompt(GeckoSession session, String title, String msg, 1764 AuthOptions options, AuthCallback callback); 1765 1766 class Choice { 1767 /** 1768 * Display choices in a menu that dismisses as soon as an item is chosen. 1769 */ 1770 public static final int CHOICE_TYPE_MENU = 1; 1771 1772 /** 1773 * Display choices in a list that allows a single selection. 1774 */ 1775 public static final int CHOICE_TYPE_SINGLE = 2; 1776 1777 /** 1778 * Display choices in a list that allows multiple selections. 1779 */ 1780 public static final int CHOICE_TYPE_MULTIPLE = 3; 1781 1782 /** 1783 * A boolean indicating if the item is disabled. Item should not be 1784 * selectable if this is true. 1785 */ 1786 public final boolean disabled; 1787 1788 /** 1789 * A String giving the URI of the item icon, or null if none exists 1790 * (only valid for menus) 1791 */ 1792 public final String icon; 1793 1794 /** 1795 * A String giving the ID of the item or group 1796 */ 1797 public final String id; 1798 1799 /** 1800 * A Choice array of sub-items in a group, or null if not a group 1801 */ 1802 public final Choice[] items; 1803 1804 /** 1805 * A string giving the label for displaying the item or group 1806 */ 1807 public final String label; 1808 1809 /** 1810 * A boolean indicating if the item should be pre-selected 1811 * (pre-checked for menu items) 1812 */ 1813 public final boolean selected; 1814 1815 /** 1816 * A boolean indicating if the item should be a menu separator 1817 * (only valid for menus) 1818 */ 1819 public final boolean separator; 1820 Choice(GeckoBundle choice)1821 /* package */ Choice(GeckoBundle choice) { 1822 disabled = choice.getBoolean("disabled"); 1823 icon = choice.getString("icon"); 1824 id = choice.getString("id"); 1825 label = choice.getString("label"); 1826 selected = choice.getBoolean("selected"); 1827 separator = choice.getBoolean("separator"); 1828 1829 GeckoBundle[] choices = choice.getBundleArray("items"); 1830 if (choices == null) { 1831 items = null; 1832 } else { 1833 items = new Choice[choices.length]; 1834 for (int i = 0; i < choices.length; i++) { 1835 items[i] = new Choice(choices[i]); 1836 } 1837 } 1838 } 1839 } 1840 1841 /** 1842 * Callback interface for notifying the result of menu or list choice. 1843 */ 1844 interface ChoiceCallback extends AlertCallback { 1845 /** 1846 * Called by the prompt implementation when the menu or single-choice list is 1847 * dismissed by the user. 1848 * 1849 * @param id ID of the selected item. 1850 */ confirm(String id)1851 void confirm(String id); 1852 1853 /** 1854 * Called by the prompt implementation when the multiple-choice list is 1855 * dismissed by the user. 1856 * 1857 * @param ids IDs of the selected items. 1858 */ confirm(String[] ids)1859 void confirm(String[] ids); 1860 1861 /** 1862 * Called by the prompt implementation when the menu or single-choice list is 1863 * dismissed by the user. 1864 * 1865 * @param item Choice representing the selected item; must be an original 1866 * Choice object that was passed to the implementation. 1867 */ confirm(Choice item)1868 void confirm(Choice item); 1869 1870 /** 1871 * Called by the prompt implementation when the multiple-choice list is 1872 * dismissed by the user. 1873 * 1874 * @param items Choice array representing the selected items; must be original 1875 * Choice objects that were passed to the implementation. 1876 */ confirm(Choice[] items)1877 void confirm(Choice[] items); 1878 } 1879 1880 1881 /** 1882 * Display a menu prompt or list prompt. 1883 * 1884 * @param session GeckoSession that triggered the prompt 1885 * @param title Title for the prompt dialog, or null for no title. 1886 * @param msg Message for the prompt dialog, or null for no message. 1887 * @param type One of CHOICE_TYPE_* indicating the type of prompt. 1888 * @param choices Array of Choices each representing an item or group. 1889 * @param callback Callback interface. 1890 */ onChoicePrompt(GeckoSession session, String title, String msg, int type, Choice[] choices, ChoiceCallback callback)1891 void onChoicePrompt(GeckoSession session, String title, String msg, int type, 1892 Choice[] choices, ChoiceCallback callback); 1893 1894 /** 1895 * Display a color prompt. 1896 * 1897 * @param session GeckoSession that triggered the prompt 1898 * @param title Title for the prompt dialog. 1899 * @param value Initial color value in HTML color format. 1900 * @param callback Callback interface; the result passed to confirm() must be in 1901 * HTML color format. 1902 */ onColorPrompt(GeckoSession session, String title, String value, TextCallback callback)1903 void onColorPrompt(GeckoSession session, String title, String value, 1904 TextCallback callback); 1905 1906 /** 1907 * Prompt for year, month, and day. 1908 */ 1909 static final int DATETIME_TYPE_DATE = 1; 1910 1911 /** 1912 * Prompt for year and month. 1913 */ 1914 static final int DATETIME_TYPE_MONTH = 2; 1915 1916 /** 1917 * Prompt for year and week. 1918 */ 1919 static final int DATETIME_TYPE_WEEK = 3; 1920 1921 /** 1922 * Prompt for hour and minute. 1923 */ 1924 static final int DATETIME_TYPE_TIME = 4; 1925 1926 /** 1927 * Prompt for year, month, day, hour, and minute, without timezone. 1928 */ 1929 static final int DATETIME_TYPE_DATETIME_LOCAL = 5; 1930 1931 /** 1932 * Display a date/time prompt. 1933 * 1934 * @param session GeckoSession that triggered the prompt 1935 * @param title Title for the prompt dialog; currently always null. 1936 * @param type One of DATETIME_TYPE_* indicating the type of prompt. 1937 * @param value Initial date/time value in HTML date/time format. 1938 * @param min Minimum date/time value in HTML date/time format. 1939 * @param max Maximum date/time value in HTML date/time format. 1940 * @param callback Callback interface; the result passed to confirm() must be in 1941 * HTML date/time format. 1942 */ onDateTimePrompt(GeckoSession session, String title, int type, String value, String min, String max, TextCallback callback)1943 void onDateTimePrompt(GeckoSession session, String title, int type, 1944 String value, String min, String max, TextCallback callback); 1945 1946 /** 1947 * Callback interface for notifying the result of file prompts. 1948 */ 1949 interface FileCallback extends AlertCallback { 1950 /** 1951 * Called by the prompt implementation when the user makes a file selection in 1952 * single-selection mode. 1953 * 1954 * @param context An application Context for parsing URIs. 1955 * @param uri The URI of the selected file. 1956 */ confirm(Context context, Uri uri)1957 void confirm(Context context, Uri uri); 1958 1959 /** 1960 * Called by the prompt implementation when the user makes file selections in 1961 * multiple-selection mode. 1962 * 1963 * @param context An application Context for parsing URIs. 1964 * @param uris Array of URI objects for the selected files. 1965 */ confirm(Context context, Uri[] uris)1966 void confirm(Context context, Uri[] uris); 1967 } 1968 1969 static final int FILE_TYPE_SINGLE = 1; 1970 static final int FILE_TYPE_MULTIPLE = 2; 1971 1972 /** 1973 * Display a file prompt. 1974 * 1975 * @param session GeckoSession that triggered the prompt 1976 * @param title Title for the prompt dialog. 1977 * @param type One of FILE_TYPE_* indicating the prompt type. 1978 * @param mimeTypes Array of permissible MIME types for the selected files, in 1979 * the form "type/subtype", where "type" and/or "subtype" can be 1980 * "*" to indicate any value. 1981 * @param callback Callback interface. 1982 */ onFilePrompt(GeckoSession session, String title, int type, String[] mimeTypes, FileCallback callback)1983 void onFilePrompt(GeckoSession session, String title, int type, 1984 String[] mimeTypes, FileCallback callback); 1985 } 1986 1987 /** 1988 * GeckoSession applications implement this interface to handle content scroll 1989 * events. 1990 **/ 1991 public interface ScrollDelegate { 1992 /** 1993 * The scroll position of the content has changed. 1994 * 1995 * @param session GeckoSession that initiated the callback. 1996 * @param scrollX The new horizontal scroll position in pixels. 1997 * @param scrollY The new vertical scroll position in pixels. 1998 */ onScrollChanged(GeckoSession session, int scrollX, int scrollY)1999 public void onScrollChanged(GeckoSession session, int scrollX, int scrollY); 2000 } 2001 2002 private final TrackingProtection mTrackingProtection = new TrackingProtection(this); 2003 2004 /** 2005 * GeckoSession applications implement this interface to handle tracking 2006 * protection events. 2007 **/ 2008 public interface TrackingProtectionDelegate { 2009 static final int CATEGORY_AD = 1 << 0; 2010 static final int CATEGORY_ANALYTIC = 1 << 1; 2011 static final int CATEGORY_SOCIAL = 1 << 2; 2012 static final int CATEGORY_CONTENT = 1 << 3; 2013 2014 /** 2015 * A tracking element has been blocked from loading. 2016 * 2017 * @param session The GeckoSession that initiated the callback. 2018 * @param uri The URI of the blocked element. 2019 * @param categories The tracker categories of the blocked element. 2020 * One or more of the {@link TrackingProtectionDelegate#CATEGORY_AD} 2021 * flags. 2022 */ onTrackerBlocked(GeckoSession session, String uri, int categories)2023 void onTrackerBlocked(GeckoSession session, String uri, int categories); 2024 } 2025 2026 /** 2027 * Enable tracking protection. 2028 * @param categories The categories of trackers that should be blocked. 2029 * Use one or more of the {@link TrackingProtectionDelegate#CATEGORY_AD} 2030 * flags. 2031 **/ enableTrackingProtection(int categories)2032 public void enableTrackingProtection(int categories) { 2033 mTrackingProtection.enable(categories); 2034 } 2035 2036 /** 2037 * Disable tracking protection. 2038 **/ disableTrackingProtection()2039 public void disableTrackingProtection() { 2040 mTrackingProtection.disable(); 2041 } 2042 2043 /** 2044 * GeckoSession applications implement this interface to handle requests for permissions 2045 * from content, such as geolocation and notifications. For each permission, usually 2046 * two requests are generated: one request for the Android app permission through 2047 * requestAppPermissions, which is typically handled by a system permission dialog; 2048 * and another request for the content permission (e.g. through 2049 * requestContentPermission), which is typically handled by an app-specific 2050 * permission dialog. 2051 **/ 2052 public interface PermissionDelegate { 2053 /** 2054 * Permission for using the geolocation API. 2055 * See: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation 2056 */ 2057 public static final int PERMISSION_GEOLOCATION = 0; 2058 2059 /** 2060 * Permission for using the notifications API. 2061 * See: https://developer.mozilla.org/en-US/docs/Web/API/notification 2062 */ 2063 public static final int PERMISSION_DESKTOP_NOTIFICATION = 1; 2064 2065 /** 2066 * Callback interface for notifying the result of a permission request. 2067 */ 2068 interface Callback { 2069 /** 2070 * Called by the implementation after permissions are granted; the 2071 * implementation must call either grant() or reject() for every request. 2072 */ grant()2073 void grant(); 2074 2075 /** 2076 * Called by the implementation when permissions are not granted; the 2077 * implementation must call either grant() or reject() for every request. 2078 */ reject()2079 void reject(); 2080 } 2081 2082 /** 2083 * Request Android app permissions. 2084 * 2085 * @param session GeckoSession instance requesting the permissions. 2086 * @param permissions List of permissions to request; possible values are, 2087 * android.Manifest.permission.ACCESS_COARSE_LOCATION 2088 * android.Manifest.permission.ACCESS_FINE_LOCATION 2089 * android.Manifest.permission.CAMERA 2090 * android.Manifest.permission.RECORD_AUDIO 2091 * @param callback Callback interface. 2092 */ onAndroidPermissionsRequest(GeckoSession session, String[] permissions, Callback callback)2093 void onAndroidPermissionsRequest(GeckoSession session, String[] permissions, 2094 Callback callback); 2095 2096 /** 2097 * Request content permission. 2098 * 2099 * @param session GeckoSession instance requesting the permission. 2100 * @param uri The URI of the content requesting the permission. 2101 * @param type The type of the requested permission; possible values are, 2102 * PERMISSION_GEOLOCATION 2103 * PERMISSION_DESKTOP_NOTIFICATION 2104 * @param access Not used. 2105 * @param callback Callback interface. 2106 */ onContentPermissionRequest(GeckoSession session, String uri, int type, String access, Callback callback)2107 void onContentPermissionRequest(GeckoSession session, String uri, int type, 2108 String access, Callback callback); 2109 2110 class MediaSource { 2111 /** 2112 * The media source is a camera. 2113 */ 2114 public static final int SOURCE_CAMERA = 0; 2115 2116 /** 2117 * The media source is the screen. 2118 */ 2119 public static final int SOURCE_SCREEN = 1; 2120 2121 /** 2122 * The media source is an application. 2123 */ 2124 public static final int SOURCE_APPLICATION = 2; 2125 2126 /** 2127 * The media source is a window. 2128 */ 2129 public static final int SOURCE_WINDOW = 3; 2130 2131 /** 2132 * The media source is the browser. 2133 */ 2134 public static final int SOURCE_BROWSER = 4; 2135 2136 /** 2137 * The media source is a microphone. 2138 */ 2139 public static final int SOURCE_MICROPHONE = 5; 2140 2141 /** 2142 * The media source is audio capture. 2143 */ 2144 public static final int SOURCE_AUDIOCAPTURE = 6; 2145 2146 /** 2147 * The media source does not fall into any of the other categories. 2148 */ 2149 public static final int SOURCE_OTHER = 7; 2150 2151 /** 2152 * The media type is video. 2153 */ 2154 public static final int TYPE_VIDEO = 0; 2155 2156 /** 2157 * The media type is audio. 2158 */ 2159 public static final int TYPE_AUDIO = 1; 2160 2161 /** 2162 * A string giving the origin-specific source identifier. 2163 */ 2164 public final String id; 2165 2166 /** 2167 * A string giving the non-origin-specific source identifier. 2168 */ 2169 public final String rawId; 2170 2171 /** 2172 * A string giving the name of the video source from the system 2173 * (for example, "Camera 0, Facing back, Orientation 90"). 2174 * May be empty. 2175 */ 2176 public final String name; 2177 2178 /** 2179 * An int giving the media source type. 2180 * Possible values for a video source are: 2181 * SOURCE_CAMERA, SOURCE_SCREEN, SOURCE_APPLICATION, SOURCE_WINDOW, SOURCE_BROWSER, and SOURCE_OTHER. 2182 * Possible values for an audio source are: 2183 * SOURCE_MICROPHONE, SOURCE_AUDIOCAPTURE, and SOURCE_OTHER. 2184 */ 2185 public final int source; 2186 2187 /** 2188 * An int giving the type of media, must be either TYPE_VIDEO or TYPE_AUDIO. 2189 */ 2190 public final int type; 2191 getSourceFromString(String src)2192 private static int getSourceFromString(String src) { 2193 // The strings here should match those in MediaSourceEnum in MediaStreamTrack.webidl 2194 if ("camera".equals(src)) { 2195 return SOURCE_CAMERA; 2196 } else if ("screen".equals(src)) { 2197 return SOURCE_SCREEN; 2198 } else if ("application".equals(src)) { 2199 return SOURCE_APPLICATION; 2200 } else if ("window".equals(src)) { 2201 return SOURCE_WINDOW; 2202 } else if ("browser".equals(src)) { 2203 return SOURCE_BROWSER; 2204 } else if ("microphone".equals(src)) { 2205 return SOURCE_MICROPHONE; 2206 } else if ("audioCapture".equals(src)) { 2207 return SOURCE_AUDIOCAPTURE; 2208 } else if ("other".equals(src)) { 2209 return SOURCE_OTHER; 2210 } else { 2211 throw new IllegalArgumentException("String: " + src + " is not a valid media source string"); 2212 } 2213 } 2214 getTypeFromString(String type)2215 private static int getTypeFromString(String type) { 2216 // The strings here should match the possible types in MediaDevice::MediaDevice in MediaManager.cpp 2217 if ("video".equals(type)) { 2218 return TYPE_VIDEO; 2219 } else if ("audio".equals(type)) { 2220 return TYPE_AUDIO; 2221 } else { 2222 throw new IllegalArgumentException("String: " + type + " is not a valid media type string"); 2223 } 2224 } 2225 MediaSource(GeckoBundle media)2226 /* package */ MediaSource(GeckoBundle media) { 2227 id = media.getString("id"); 2228 rawId = media.getString("rawId"); 2229 name = media.getString("name"); 2230 source = getSourceFromString(media.getString("source")); 2231 type = getTypeFromString(media.getString("type")); 2232 } 2233 } 2234 2235 /** 2236 * Callback interface for notifying the result of a media permission request, 2237 * including which media source(s) to use. 2238 */ 2239 interface MediaCallback { 2240 /** 2241 * Called by the implementation after permissions are granted; the 2242 * implementation must call one of grant() or reject() for every request. 2243 * 2244 * @param video "id" value from the bundle for the video source to use, 2245 * or null when video is not requested. 2246 * @param audio "id" value from the bundle for the audio source to use, 2247 * or null when audio is not requested. 2248 */ grant(final String video, final String audio)2249 void grant(final String video, final String audio); 2250 2251 /** 2252 * Called by the implementation after permissions are granted; the 2253 * implementation must call one of grant() or reject() for every request. 2254 * 2255 * @param video MediaSource for the video source to use (must be an original 2256 * MediaSource object that was passed to the implementation); 2257 * or null when video is not requested. 2258 * @param audio MediaSource for the audio source to use (must be an original 2259 * MediaSource object that was passed to the implementation); 2260 * or null when audio is not requested. 2261 */ grant(final MediaSource video, final MediaSource audio)2262 void grant(final MediaSource video, final MediaSource audio); 2263 2264 /** 2265 * Called by the implementation when permissions are not granted; the 2266 * implementation must call one of grant() or reject() for every request. 2267 */ reject()2268 void reject(); 2269 } 2270 2271 /** 2272 * Request content media permissions, including request for which video and/or 2273 * audio source to use. 2274 * 2275 * @param session GeckoSession instance requesting the permission. 2276 * @param uri The URI of the content requesting the permission. 2277 * @param video List of video sources, or null if not requesting video. 2278 * @param audio List of audio sources, or null if not requesting audio. 2279 * @param callback Callback interface. 2280 */ onMediaPermissionRequest(GeckoSession session, String uri, MediaSource[] video, MediaSource[] audio, MediaCallback callback)2281 void onMediaPermissionRequest(GeckoSession session, String uri, MediaSource[] video, 2282 MediaSource[] audio, MediaCallback callback); 2283 } 2284 } 2285