apache / cordova-plugin-inappbrowser

Apache Cordova InAppBrowser Plugin
https://cordova.apache.org/
Apache License 2.0
1.11k stars 2.14k forks source link

message event fires only once for same inAppBrowser window #539

Open julia-fix opened 4 years ago

julia-fix commented 4 years ago

Bug Report

Problem

When message event listener contains another inAppBrowser call to open link in system browser, message event fires only once for one inAppBrowser window

What is expected to happen?

Every time when postMessage is executed in inAppBrowser window, message event listener would be fired.

What does actually happen?

Only first message is received by message event listener.

Information

Command or Code

This is code from remote web page that is opened in inAppBrowser:

$('.share-button').on('click', function(e) {
    var href = $(this).attr('href');
     e.preventDefault();
    console.log('sending message', href);
    webkit.messageHandlers.cordova_iab.postMessage(JSON.stringify({url: href}));
    return false;
 });

Code in application that calls inAppBrowser window and listens to message event:

element.on('click', function(e) {
    e.preventDefault();
    var browserWindow = cordova.InAppBrowser.open(element.attr('href'), element.attr('target'), 'location=no,zoom=no');

    browserWindow.addEventListener('message', function(params) {
        console.log('params', params);
        if (params.data.url) {
            cordova.InAppBrowser.open(params.data.url, '_system');
        } 
    });

    return false;
});

Environment, Platform, Device

Platform: Android. Tested on emulator (API 26) and real device (Android 8.1) with same result.

Version information

Cordova: 9.0.0 (cordova-lib@9.0.1)

cordova-plugin-inappbrowser 3.1.0 "InAppBrowser"

Cordova platform: android 7.1.4

Checklist

diesieben07 commented 4 years ago

I just ran into this as well. This happens because the plugin can only store one reference to a cordova callback context. If you open something in the system browser, it overwrites this reference, even though no events will ever be fired for the system browser window.

As a workaround you need to update your event subscriptions to the new object, returned from the call to open the _system window. This reference will fire the events for the still existing, initial, in app browser window.

I hope this makes sense.

flowMartin commented 4 years ago

Hi @diesieben07

I am also dealing with the same problem. Would you mind providing a code example of how to update the event subscriptions?

Thanks

diesieben07 commented 4 years ago

I am using Ionic, so you'll have to adapt this if you are using plain cordova. I am also just using pseudocode here, but it should be clear:

class MyComponent {
  private iabSubscriptions = [];
  private iabObject: InAppBrowserObject|null = null;

  private subscribeIab(iab: InAppBrowserObject)
    // unsubscribe from the old IAB object
    for (const s of iabSubscriptions) {
      s.unsubscribe();
    }
    this.iabObject = iab;
    this.iabSubscriptions = [
      // do your subscriptions here
      iab.on('message').subscribe(arg => {
        const newIab = this.inAppBrowser.create(/* new url here */, '_system');
        this.subscribeIab(newIab);
      })
    ]; 
  }

  private openIab(url: string) {
    this.subscribeIab(this.inAppBrowser.create(url));
  }

  private closeIab() {
    if (this.iabObject) {
      this.iabObject.close();
      this.iabObject = null;
    }
  }
}
rigiddesign commented 4 years ago

I just ran into this as well. This happens because the plugin can only store one reference to a cordova callback context. If you open something in the system browser, it overwrites this reference, even though no events will ever be fired for the system browser window.

As a workaround you need to update your event subscriptions to the new object, returned from the call to open the _system window. This reference will fire the events for the still existing, initial, in app browser window.

I hope this makes sense.

Absolute legend. Thanks for sharing, that fixed it for me!

sardapv commented 3 years ago

@diesieben07 you are an angel!! thank you very much!

fburian commented 3 months ago

Unfortunately, this is not possible under Cordova because iab.on('message') does not exist as a function.

I simply start a new instance of the InAppBrowser when the app comes back to the foreground from the background. I make sure internally that I cache the current URL so that I always reload the last page.

The code could look something like this:

var currentUrl = 'https://example.com';

document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() { // Start default page openIAB(currentUrl); // Event when app goes to foreground document.addEventListener('resume', resumeToApp, false);
}

function openIAB(url) { // Open InAppBrowser var iab = cordova.InAppBrowser.open(url, "_blank", 'usewkwebview=yes,hidden=yes,hardwareback=no,toolbar=no,hidenavigationbuttons=yes'); // Store current url iab.addEventListener('loadstart', function(e) { currentUrl = e.url; }); }

function resumeToApp() { openIAB(currentUrl); }