Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Engine events that require a callback #1110

Closed
pocmo opened this issue Oct 19, 2018 · 5 comments
Closed

Engine events that require a callback #1110

pocmo opened this issue Oct 19, 2018 · 5 comments
Assignees
Labels
🔬 Research Problems without a solution that need research.
Milestone

Comments

@pocmo
Copy link
Contributor

pocmo commented Oct 19, 2018

So far events coming out of the engine (callbacks getting called in delegates) added/changed/updated some data and we used that to update the state in Session.

There are some upcoming engine feature where we are required to keep a reference to a callback that the engine gives to us. Fullscreen mode was an example of that where WebView gave us a CustomViewCallback. In this case we were able to keep the callback internally and the app side didn't need to deal with that. The upcoming features (e.g. open/close window #1067, geolocation #1078, file uploads #1076, permission requests #1157) may require that the app gets some kind of way to execute those callbacks. And I don't think we want to stuff the callbacks into Session which is just a plain holder of the current state.

┆Issue is synchronized with this Jira Task

@pocmo pocmo added the 🔬 Research Problems without a solution that need research. label Oct 19, 2018
@pocmo pocmo modified the milestones: ⚖️ Planning, 0.29 🏁 Oct 19, 2018
@pocmo pocmo self-assigned this Oct 22, 2018
@pocmo
Copy link
Contributor Author

pocmo commented Oct 26, 2018

Let's look at the APIs in more detail:

Geolocation requests

What needs to happen?

  • The app needs to show some kind of prompt UI asking the user for permission

  • Once the user makes a decision the callback needs to be invoked

  • If the user has permanently accepted the permission for the uri/origin then the callback can get executed immediately

  • Navigating away will dismiss the request

  • Side effect: The app may request the runtime permission from the system to be able to get location updates. GeckoView has a separate callback for that (onAndroidPermissionsRequest) while it looks like WebView expects you to do that manually.

GeckoView

  • onContentPermissionRequest(GeckoSession session, String uri, @Permission int type, String access, Callback callback)
  • Callback.grant()
  • Callback.reject()

WebView

  • void onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback)
  • GeolocationPermissions.Callback.invoke(String origin, boolean allow, boolean retain)

File uploads

What needs to happen?

  • The app needs to show a file prompt (most likely the system prompt)
  • Once the user selects one or multiple files from the prompt the callbacks need to get executed
  • Navigating away will dismiss the request

GeckoView

  • onFilePrompt(GeckoSession session, String title, int type, String[] mimeTypes, FileCallback callback)
  • FileCallback.confirm(Context context, Uri uri)
  • FileCallback.confirm(Context context, Uri[] uris)

WebView

  • boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
  • ValueCallback.onReceiveValue(Uri[] value)

Permission requests

What needs to happen?

  • The app needs to show some kind of prompt UI asking the user for permission. The prompt is more advanced because the user may be able to select between multiple video/audio sources.

  • Once the user makes a decision the callback needs to be invoked.

  • Navigating away will dismiss the request

  • Side effect: The app may request the runtime permission from the system to be able to get location updates. GeckoView has a separate callback for that (onAndroidPermissionsRequest) while it looks like WebView expects you to do that manually.

GeckoView

  • onMediaPermissionRequest(GeckoSession session, String uri, MediaSource[] video, MediaSource[] audio, MediaCallback callback)
  • MediaCallback.grant(final String video, final String audio)
  • MediaCallback.grant(final MediaSource video, final MediaSource audio)
  • MediaCallback.reject()

WebView

  • void onPermissionRequest (PermissionRequest request)
  • void onPermissionRequestCanceled (PermissionRequest request)
  • PermissionRequest.deny()
  • PermissionRequest.grant(String[] resources)

Windows

What needs to happen?

  • The app needs to create a new Session (Tab) for the window (with parent ID).
  • The callback needs to be executed notifying the engine about the new GeckoSession/WebView.
  • No user interaction may be required,.
  • Navigating away will dismiss the request

GeckoView

  • GeckoResult<GeckoSession> onNewSession(@NonNull GeckoSession session, @NonNull String uri)

WebView

  • boolean onCreateWindow (WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
  • resultMsg.object has a WebView.WebViewTransport that needs new WebView via WebView.WebViewTransport.setWebView(WebView)

@pocmo
Copy link
Contributor Author

pocmo commented Oct 26, 2018

Observations:

  • Some events may require state (in browser-session?) to render UI pieces like prompts
  • Most callbacks will require user input from UI

@pocmo
Copy link
Contributor Author

pocmo commented Oct 26, 2018

I wonder if we could put some state into session (e.g. "permission request for camera etc.") and I can use that state (or an ID in it?) to invoke a usecase / engine method that will invoke the callback (actual callbacks are held internally in engine and not exposed).

@csadilek
Copy link
Contributor

Summarizing our current plan on how to implement these engine callbacks:

We'll use our existing EngineSession.Observer and add methods as needed:

interface Observer {
  ...
  fun onFilePrompt(filePromptRequest: FilePromptRequest) = Unit
  fun onShowGeoPermissionPrompt(geoPromptRequest: GeoPromptRequest)
  fun onHideGeoPermissionPrompt(geoPromptRequest: GeoPromptRequest)
  ...
}

These *Request objects are abstractions with implementations for both GeckoView and WebView and will contain methods to access the relevant data and to resolve the requests.

class GeckoFilePrompt(private val context: Context,
                      private val title: String,
                      private val type: Int,
                      private val mimetype: Array<String>,
                      private val callback: GeckoSession.PromptDelegate.FileCallback
) : FilePrompt {

    override fun confirm(uris: Array<Uri>) {
        callback.confirm(context, uris)
    }
}
class SystemFilePrompt(private val callback: ValueCallback<Array<Uri>>,
                       private val fileChooserParams: WebChromeClient.FileChooserParams
) : FilePrompt {

    override fun confirm(uris: Array<Uri>) {
        callback.onReceiveValue(uris)
    }
}

When these observer methods are called, EngineObserver will be notified, which will in turn update the corresponding browser session by setting Consumable fields. This is the same logical flow as for any other engine session events.

override fun onFilePrompt(filePromptRequest: FilePromptRequest) {
  session.filePrompt = Consumable.from(filePromptRequest)
}

All components observing browser session changes (currently all Presenters) can then react and consume these requests by showing the prompt and resolving the request/callback. It's important that we only consume if the request was successfully resolved. Consumable.consume already accepts a Lambda that has to return true for the value to be consumed. We can use that or create a new method tryConsume will only consume if no exception is thrown. An important distinction between these session fields and others is that they are transient i.e. we don't want to persist them to storage.

An alternative we've discussed is using POJOs (or data classes) instead of *Request objects and add methods to the EngineSession to resolve these "requests". Both approaches should work, but we decided to try the former first as it seems simpler.

@pocmo
Copy link
Contributor Author

pocmo commented Oct 26, 2018

Thank you for the summary. Closing this issue because we have a plan now. In the next sprint we want to pick one or multiple of the mentioned callbacks and implement them.

@pocmo pocmo closed this as completed Oct 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🔬 Research Problems without a solution that need research.
Projects
None yet
Development

No branches or pull requests

2 participants