Skip to content

[WebView] onMessage failing when there are JS warnings and errors on the page #10865

Closed

Description

@raiderrobert

Description

I'm receiving this error (same as screenshot below):

Setting onMessage on a WebView overrides existing values of window.postMessage, 
but a previous value was defined.

screen shot 2016-11-10 at 3 33 31 pm

That specific error message is found in the code base here: https://github.com/facebook/react-native/blob/master/React/Views/RCTWebView.m#L286

Implementation

I followed the example at this link for onMessage at this link: http://facebook.github.io/react-native/releases/0.37/docs/webview.html#examples

I made a callable respondToOnMessage and injected some JavaScript.

class MessagingTest extends React.Component {

    respondToOnMessage = e =>{
        console.log(e);
    };

    render() {
        const jsCode = `window.postMessage('test');`;

        return (
        <WebView
        injectedJavaScript={jsCode}
        source={uri: 'https://www.foo.com/'}}
        onMessage={this.respondToOnMessage}
        />
        )
    }
}

Reproduction

I load the app with this example and I have it pointed to a website (rather not say which one. Sorry.) And the website is emitting 2 errors into the Chrome console when I go there.

(index):994 A Parser-blocking, cross-origin script, http://example.com/thing.js, is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity.

widgets.js:8 Uncaught TypeError: Cannot set property '[object Array]' of undefined(…)

Other websites like google.com and github.com are just fine. If you want to replicate it, change the uri to yahoo.com

Additional Information

  • React Native version: 0.37
  • Platform: iOS 10.1, iPhone 6 emulator
  • Operating System: Mac OSX Sierra 10.12.1

Activity

  1. raiderrobert commented on Nov 10, 2016

    @raiderrobert
    Author

    I should also add that I attempted to replicate the error on Android 7.0 and API 24 using a Nexus_5x emulator, and I got NO error. So this seems to be a platform specific error.

  2. sreucherand commented on Nov 11, 2016

    @sreucherand

    I'm getting the same error. Seems like this exception is thrown when window.postMessage has been overwritten. But since webViewDidFinishLoad is called multiple times, it tries to overwrite window.postMessage over and over. Not sure though. Can we have you expertise @jacobp100?

  3. jacobp100 commented on Nov 11, 2016

    @jacobp100
    Contributor

    It’s because the page you’re visiting is overriding postMessage, which may be due to a shim. Showing an error was chosen over invoking the shim’s value of postMessage when you call the value of postMessage that we set.

    I thought I’d made it easier to workaround this problem, but it looks like that won’t work. The best you can do at the moment, which is an awful hack, is,

    injectedJavaScript="window.postMessage = String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');"

    Or just fork the project and remove the code you pointed to. Whatever’s easiest for you!

    Ping @satya164 for input on how to resolve.

  4. sreucherand commented on Nov 11, 2016

    @sreucherand

    Well, when I try String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage') on the page I visit, it returns true, so I am guessing postMessage has not been overridden.

  5. jacobp100 commented on Nov 11, 2016

    @jacobp100
    Contributor

    Oh I see. The JS is injected after postMessage is overwritten. Your best bet is to fork it then, and I’ll see what I can do about getting this fixed! Sorry. :(

  6. persyl commented on Nov 20, 2016

    @persyl

    I have same error, without injecting any javascript to WebView - just setting WebView-properties:

    source={require('../lib/html/somefile.html')}
    onMessage={() => console.log('Hello World')}

    and in my ../lib/html/somefile.html' I have javascript code doing both window.postMessage() and document.addEventListener("message",.......

    Everything worked fine when I had the HTML in a variable "someHtml", like:
    source={{html: someHtml}}

    So difference seem to be when changing source to HTML-file instead of HTML-string.

    Still for me I had other reasons, not related to this, why I really need to read HTML from file instead of string so it would be great to get this issue fixed.

  7. cbcye commented on Nov 20, 2016

    @cbcye

    +1, still has this problem

  8. jacobp100 commented on Nov 20, 2016

    @jacobp100
    Contributor

    There is an ongoing discussion in #10941 for the best way to go forward with this.

    It won't be in the next release or probably the release after. If you need this fixed now, fork the project and use the fix in the PR.

  9. lealife commented on Dec 19, 2016

    @lealife

    +1, I'm getting the same error. When there is a iframe on web, it occurs the same error

  10. varhp commented on Dec 21, 2016

    @varhp

    +1,encounter this problem

  11. jacobp100 commented on Dec 21, 2016

    @jacobp100
    Contributor

    Again, there is a pr for this. We're aware this problem is affecting a lot of people.

  12. edencakir commented on Dec 26, 2016

    @edencakir

    +1 getting the same error.

  13. kyle-ilantzis commented on Dec 31, 2016

    @kyle-ilantzis

    injectedJavascript should instead be the following

    (function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage'); 
      };
    
      window.postMessage = patchedPostMessage;
    })();

    And then it works! 😄

    And if any one is interested, in my code instead of writing a giant string I did the following

    const patchPostMessageFunction = function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    
    ...
    
    <WebView injectedJavaScript={patchPostMessageJsCode} />
  14. mschipperheyn commented on Jan 21, 2017

    @mschipperheyn

    @kyle-ilantzis tried this with RN 0.38.1, but still get the Red Screen. What version of RN are you on?

  15. 82 remaining items

  16. AdamZaczek commented on Jan 23, 2019

    @AdamZaczek

    injectedJavascript should instead be the following

    (function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage'); 
      };
    
      window.postMessage = patchedPostMessage;
    })();

    And then it works! 😄

    And if any one is interested, in my code instead of writing a giant string I did the following

    const patchPostMessageFunction = function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    
    ...
    
    <WebView injectedJavaScript={patchPostMessageJsCode} />

    You man are my hero! This is so simple and eloquent. It works for Expo 31 even when using iframes

  17. olegongit commented on Feb 6, 2019

    @olegongit

    For me it helped to use 'useWebKit' prop on a WebView.

  18. princeTPG commented on Feb 12, 2019

    @princeTPG

    This issue will not occur is you use "useWebKit" prop of webview, that will use WK webview instead of UI webview in case of IOS.
    But in case if you don't want to use WK webview, then below is the sample JS code that you can inject in you Webview with "injectedJavaScript" prop.

      `(function () {
          let originalPostMessage = window.postMessage;
          let patchedPostMessage = function(message, targetOrigin, transfer, ...other) { 
            originalPostMessage(message, targetOrigin, transfer, ...other);
          };
    
          patchedPostMessage.toString = function() { 
            return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
          };
    
          window.postMessage = patchedPostMessage;
        })()`
    

    The above code did nothing just patched window.postMessage function, the above issue will not appear.

  19. Angelk90 commented on Apr 1, 2019

    @Angelk90

    @AdamZaczek , @princeTPG :

    I can't get it to work properly, you can give an example that can work on https://snack.expo.io?

    I did something like that, but it seems that it doesn't work anymore.

    Code:

    render() {
        const jsCodeOpenload = `
            (function ready() {
              function whenRNPostMessageReady(cb) {
                if (postMessage.length === 1) cb();
                else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
              }
              whenRNPostMessageReady(function() {
                postMessage(document.getElementById('olvideo_html5_api').innerHTML);
              });
            })();`;
        return (
          <View>
            {this.state.urlOpenload != '' && (
              <WebView
                source={{
                  uri: this.state.urlOpenload,
                }}
                onMessage={evt => {
                  this.setState({ code: evt.nativeEvent.data });
                }}
                injectedJavaScript={jsCodeOpenload}
                javaScriptEnabled
                style={{ flex: 1 }}
              />
            )}
          </View>
        );
      }
    

    onMessage seems to receive nothing more, even if it worked before.

  20. TfADrama commented on May 22, 2019

    @TfADrama

    This issue will not occur is you use "useWebKit" prop of webview, that will use WK webview instead of UI webview in case of IOS.
    But in case if you don't want to use WK webview, then below is the sample JS code that you can inject in you Webview with "injectedJavaScript" prop.

      `(function () {
          let originalPostMessage = window.postMessage;
          let patchedPostMessage = function(message, targetOrigin, transfer, ...other) { 
            originalPostMessage(message, targetOrigin, transfer, ...other);
          };
    
          patchedPostMessage.toString = function() { 
            return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
          };
    
          window.postMessage = patchedPostMessage;
        })()`
    

    The above code did nothing just patched window.postMessage function, the above issue will not appear.

    The patched script makes my onMessage to be always called twice in iOS when i do a click event...
    The useWebKit prop was the only way, at least for now...

  21. kenobi91 commented on Jun 27, 2019

    @kenobi91

    If you still have this issue you can use code from above, to prevent this error like this:

    `const patchPostMessageFunction = function() {
    var originalPostMessage = window.postMessage;

    var patchedPostMessage = function(message, targetOrigin, transfer) {
    originalPostMessage(message, targetOrigin, transfer);
    };

    patchedPostMessage.toString = function() {
    return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
    };

    window.postMessage = patchedPostMessage;
    };`

    Then you can append your own JS injection code like this:

    <WebView injectedJavaScript={'(' + String(patchPostMessageFunction) + ')();' + injectedJavaScript}/>

    This will prevent error from above but also this will prevent in some cases to use window.postMessage from your injected JS code. To go around this just wrap window.postMessage(msg) in timeout like this:
    setTimeout(() => { window.postMessage(JSON.stringify(postMessage)); }, 100);

  22. Rafaell416 commented on Jul 29, 2019

    @Rafaell416

    injectedJavascript should instead be the following

    (function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage'); 
      };
    
      window.postMessage = patchedPostMessage;
    })();

    And then it works! 😄

    And if any one is interested, in my code instead of writing a giant string I did the following

    const patchPostMessageFunction = function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    
    ...
    
    <WebView injectedJavaScript={patchPostMessageJsCode} />

    Awesome man, if you come to Colombia I'll invite u some Empanadas.

  23. kckunal2612 commented on Sep 30, 2019

    @kckunal2612

    injectedJavascript should instead be the following

    (function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage'); 
      };
    
      window.postMessage = patchedPostMessage;
    })();

    And then it works! 😄

    And if any one is interested, in my code instead of writing a giant string I did the following

    const patchPostMessageFunction = function() {
      var originalPostMessage = window.postMessage;
    
      var patchedPostMessage = function(message, targetOrigin, transfer) { 
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() { 
        return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage');
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    
    ...
    
    <WebView injectedJavaScript={patchPostMessageJsCode} />

    Is there an updated version of this answer which will work with the new version of react-native-webview ? I am unable to receive messages in onMessage() on both iOS and Android. :(

  24. kckunal2612 commented on Sep 30, 2019

    @kckunal2612

    Is there an updated version of this answer which will work with the new version of react-native-webview ? I am unable to receive messages in onMessage() on both iOS and Android. :(

    Answering my own question here for everyone else's reference -

    (See - https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0)

    // Apply fix for newer react-native-webview not supporting window.postMessage
    const webViewFix = function() {
      window.postMessage = function(data) {
        window.ReactNativeWebView.postMessage(data);
      };
    };
    const windowPostMessageFix = '(' + String(webViewFix) + ')();';
    
    const patchPostMessageFunction = function() {
      const originalPostMessage = window.postMessage;
    
      const patchedPostMessage = function(message, targetOrigin, transfer) {
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() {
        return String(Object.hasOwnProperty).replace(
          'hasOwnProperty',
          'postMessage',
        );
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    

    Make sure you call windowPostMessageFix function first and then your second function -

    <WebView
     injectedJavaScript={[
                    windowPostMessageFix,
                    patchPostMessageJsCode
                  ].join('')}
    

    I hope it helps ! Thanks !

  25. DevArenaCN commented on Oct 7, 2019

    @DevArenaCN

    Is there an updated version of this answer which will work with the new version of react-native-webview ? I am unable to receive messages in onMessage() on both iOS and Android. :(

    Answering my own question here for everyone else's reference -

    (See - https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0)

    // Apply fix for newer react-native-webview not supporting window.postMessage
    const webViewFix = function() {
      window.postMessage = function(data) {
        window.ReactNativeWebView.postMessage(data);
      };
    };
    const windowPostMessageFix = '(' + String(webViewFix) + ')();';
    
    const patchPostMessageFunction = function() {
      const originalPostMessage = window.postMessage;
    
      const patchedPostMessage = function(message, targetOrigin, transfer) {
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() {
        return String(Object.hasOwnProperty).replace(
          'hasOwnProperty',
          'postMessage',
        );
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    

    Make sure you call windowPostMessageFix function first and then your second function -

    <WebView
     injectedJavaScript={[
                    windowPostMessageFix,
                    patchPostMessageJsCode
                  ].join('')}
    

    I hope it helps ! Thanks !

    This did solve the issue with the red screen, but when I tried to post data back from my frontend code calling window.postMessage('some stuff'. '*') I got error TypeError: undefined is not an object (evaluating 'window.ReactNativeWebView.postMessage') Any ideas?

  26. kckunal2612 commented on Oct 8, 2019

    @kckunal2612

    Is there an updated version of this answer which will work with the new version of react-native-webview ? I am unable to receive messages in onMessage() on both iOS and Android. :(

    Answering my own question here for everyone else's reference -
    (See - https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0)

    // Apply fix for newer react-native-webview not supporting window.postMessage
    const webViewFix = function() {
      window.postMessage = function(data) {
        window.ReactNativeWebView.postMessage(data);
      };
    };
    const windowPostMessageFix = '(' + String(webViewFix) + ')();';
    
    const patchPostMessageFunction = function() {
      const originalPostMessage = window.postMessage;
    
      const patchedPostMessage = function(message, targetOrigin, transfer) {
        originalPostMessage(message, targetOrigin, transfer);
      };
    
      patchedPostMessage.toString = function() {
        return String(Object.hasOwnProperty).replace(
          'hasOwnProperty',
          'postMessage',
        );
      };
    
      window.postMessage = patchedPostMessage;
    };
    
    const patchPostMessageJsCode = '(' + String(patchPostMessageFunction) + ')();';
    

    Make sure you call windowPostMessageFix function first and then your second function -

    <WebView
     injectedJavaScript={[
                    windowPostMessageFix,
                    patchPostMessageJsCode
                  ].join('')}
    

    I hope it helps ! Thanks !

    This did solve the issue with the red screen, but when I tried to post data back from my frontend code calling window.postMessage('some stuff'. '*') I got error TypeError: undefined is not an object (evaluating 'window.ReactNativeWebView.postMessage') Any ideas?

    I tested my app on Lollipop and it crashes due to a WebView issue listed here -
    react-native-webview/react-native-webview#858

    As a result, our company is now moving away from React Native for Android. Sadly, there are too many breaking changes and too many security vulnerabilities in third party libraries.

  27. locked as resolved and limited conversation to collaborators on Dec 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions