Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Conversation

@bparrishMines
Copy link
Contributor

@bparrishMines bparrishMines commented Oct 29, 2021

Split of #4455

The pigeon implementation and current implementation has potential leaking callbacks when a WebView is disposed. This is a possible cause of flaky tests for WebView, so this PR attempts to prevent callbacks when the corresponding WebView object is disposed. In #4455, the integration tests now seem to pass consistently. Along with fixing the resize test: #4460

This also fixes some trivial bugs found though the integration and widget tests in #4455.

Dart Portion: #4461

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Note that unlike the flutter/flutter repo, the flutter/plugins repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@google-cla google-cla bot added the cla: yes label Oct 29, 2021
@github-actions github-actions bot added p: webview_flutter Edits files for a webview_flutter plugin platform-android labels Oct 29, 2021
@bparrishMines bparrishMines changed the title Webview pigeon java Fix bugs in Java Implementation of the Pigeon API Oct 29, 2021
@bparrishMines bparrishMines changed the title Fix bugs in Java Implementation of the Pigeon API Fix bugs in Java Implementation of the Pigeon API (Java) Oct 29, 2021
@bparrishMines bparrishMines changed the title Fix bugs in Java Implementation of the Pigeon API (Java) Fix bugs in Java Implementation of the Pigeon API Oct 29, 2021
static class DownloadListenerImpl implements DownloadListener, Releasable {
private final Long instanceId;
private final DownloadListenerFlutterApiImpl flutterApi;
private boolean ignoreCallbacks = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would benefit from a comment explaining what it's for, so people don't have to go read the implementation to understand it.

return instance;
}

/** Remove the instance from the manager. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

this.flutterApi = null;
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a summary fragment

if (currentWebViewClient instanceof Releasable) {
((Releasable) currentWebViewClient).release();
}
currentWebViewClient = (WebViewClient) webViewClient;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Java have the ability to do subclass-restricted templates? You have this repeated pattern; it would be nice if you could instead have a simple smart pointer class that can only be created for things that inherit from Releasable (but is still typed with the correct specific type), that would handle the call-release-if-necessary-then-assign logic internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure what you meant by this. But, my assumption is that you were thinking something like this

private static class ReleasableChild<T> {
    @Nullable private T child;

    private ReleasableChild(@Nullable T child) {
      this.child = child;
    }

    private void release() {
      if (child instanceof Releasable) {
        ((Releasable) child).release();
      }
      child = null;
    }
  }

I replaced all the variables with this value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It looks like the Java name for what I was looking for support for is "bounded types".)

This still relies on release being called manually on every assignment, which is error-prone. I was looking for something like (probably doesn't compile as written since it's freehand):

  private static class ReleasableValue<T extends Releasable> {
    @Nullable private T value;

    ReleasableValue(@Nullable T value) {
      this.value = value;
    }

    void set(@Nullable T newValue) {
      release();
      value = newValue;
    }

    @Nullable T get() {
      return value;
    }

    void release() {
      if (value != null) {
        value.release();
      }
      value = null;
    }

    void finalize() {
      release();
    }
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok that makes sense. I changed it to:

private static class ReleasableValue<T> {
    @Nullable private T value;

    ReleasableValue() {}

    ReleasableValue(@Nullable T value) {
      this.value = value;
    }

    void set(@Nullable T newValue) {
      release();
      value = newValue;
    }

    @Nullable
    T get() {
      return value;
    }

    void release() {
      if (value instanceof Releasable) {
        ((Releasable) value).release();
      }
      value = null;
    }
  }

I removed finalize since it was only calling release. I also changed ReleasableValue<T extends Releasable> to ReleasableValue<T> because the WebView.setWebViewClient will take either a WebViewClientImpl or WebViewClientCompatImpl and their only shared interface is WebViewClient; which doesn't extend Releasable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I suppose I could just cast WebViewClient to Releasable as I set it. Maybe that is a better solution?

@bparrishMines bparrishMines changed the title Fix bugs in Java Implementation of the Pigeon API [webview_flutter_android] Fix bugs in Java Implementation of the Pigeon API Nov 9, 2021

private static class ReleasableChild<T> {
@Nullable private T child;
private static class ReleasableValue<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this using bounded types?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Which would avoid the need for the type check in release, in addition to making sure incorrect use of ReleasableValue wouldn't compile.)

public void addJavascriptInterface(Object object, String name) {
super.addJavascriptInterface(object, name);
if (object instanceof JavaScriptChannel) {
javaScriptInterfaces.put(name, new ReleasableValue<>((JavaScriptChannel) object));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this replaces a value, the old values won't get release() called until the next GC cycle; is that okay, or does any previous value need to be explicitly found and released here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Release actually needs to be called because the Dart Object would still need to be disposed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I think ReleasableValue should have a finalize that calls release.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started moving away from finalize because I remembered that it is deprecated in Java 9+: https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#finalize--. And we are already using Java 8.

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo the two comments above.

@bparrishMines bparrishMines merged commit d09abd5 into flutter:master Nov 11, 2021
@bparrishMines bparrishMines deleted the webview_pigeon_java branch November 11, 2021 00:52
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 11, 2021
amantoux pushed a commit to amantoux/plugins that referenced this pull request Dec 11, 2021
KyleFin pushed a commit to KyleFin/plugins that referenced this pull request Dec 21, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

cla: yes p: webview_flutter Edits files for a webview_flutter plugin platform-android

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants