Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

postMessage works on emulator but not on phone #221

Closed
tapir opened this issue Dec 30, 2018 · 26 comments
Closed

postMessage works on emulator but not on phone #221

tapir opened this issue Dec 30, 2018 · 26 comments
Labels
Help wanted Extra attention is needed Type: bug report

Comments

@tapir
Copy link

tapir commented Dec 30, 2018

try {
	function whenRNPostMessageReady(cb) {
		if(postMessage.length === 1) {
			cb();
		} else {
			setTimeout(function() {
				whenRNPostMessageReady(cb);
			}, 1000);
		}
	}
	whenRNPostMessageReady(function() {
		window.parent.postMessage('test-data-as-string', '*');
	});
}
catch(err) {
	document.getElementById("errr").innerHTML = err.message;
}

Above is my script block in the page I'm viewing from webview which is defined as below:

<WebView
	ref = {(ref) => {this.webViewRef = ref;}}
	startInLoadingState = {true}
	scrollEnabled = {false}
	useWebKit = {true}
	onMessage = {(e) => console.warn(e.nativeEvent.data)}
	source = {{uri: 'http://my-page-url'}}
/>

Works perfectly in emulator but does not work nor gives any error on my One Plus 5T (Oreo)

Any ideas?

@Titozzz
Copy link
Collaborator

Titozzz commented Jan 2, 2019

i'm not sure, I have a Oneplus 3T and everything is fine. Can you give me any more informations ?

@Titozzz Titozzz added the User: question Further information is requested label Jan 2, 2019
@tapir
Copy link
Author

tapir commented Jan 2, 2019

Bad news, it's not only One Plus 5T. I've tried with Galaxy S8 and it's the same.
Here is the smallest possible reproducable file and screenshots of both OnePlus 5T, Galaxy S8 and Emulator.
How can I better help debug this?

Edit: Changed the example a little bit to DOMContentLoaded just in case

import React, {Component} from 'react';
import {WebView} from 'react-native-webview';

const page = `
<html>
<head>
<script>
	function whenRNPostMessageReady(cb) {
		if(postMessage.length === 1) {
			cb();
		} else {
			setTimeout(function() {
				whenRNPostMessageReady(cb);
			}, 1000);
		}
	}
	document.addEventListener("DOMContentLoaded", function(event) {
		try {
			whenRNPostMessageReady(function() {
				window.postMessage('test-data-as-string', '*');
			});
		}
		catch(err) {
			document.getElementById('errr').innerHTML = err.message;
		}
	});
</script>
</head>
<body>
	<div id='errr'>...</div>
</body>
</html>`;

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
		<WebView
			onMessage = {(e) => console.warn(e.nativeEvent.data)}
			source = {{html: page}}
		/>
    );
  }
}

screenshot from 2019-01-02 14-01-52
photo_2019-01-02_14-05-41
photo_2019-01-02_14-05-38

@Titozzz
Copy link
Collaborator

Titozzz commented Jan 2, 2019

Can you try window.postMessage('test-data-as-string', '*'); ?

EDIT: added docs
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Syntax

@tapir
Copy link
Author

tapir commented Jan 2, 2019 via email

@tapir
Copy link
Author

tapir commented Jan 3, 2019

Changed the example to window.postMessage('data', '*') as well to avoid confusion.

Edit: At this point I'm confident that this has nothing to do with the example itself but it's a legit bug. Can we change the label from 'question' to something more appropriate?

@Titozzz
Copy link
Collaborator

Titozzz commented Jan 3, 2019

I've been able to reproduce the issue with your code. But the thing is it's working perfectly fine inside my app and I do a lot of postMessage stuff. So I'm a bit confused.

Here's how I do:

const injectedJavascript = `(function() {
  window.WebViewBridge = {
    onMessage: function() {
      return null;
    },
    send: function(data) {
      window.postMessage(data, '*');
    },
  };
  var event = new Event('WebViewBridge');
  window.dispatchEvent(event);
})()`;

const generateOnMessageFunction = data =>
  `(function() {
    window.WebViewBridge.onMessage(${JSON.stringify(data)});
  })()`;

Then to send to web (web is free to listen by overriding window.WebViewBridge.onMessage):

sendMessageToWebView = message => {
    this.webView.current.injectJavaScript(generateOnMessageFunction(message));
  };

And web can send stuff to native by:

window.WebViewBridge.send(message);

@tapir
Copy link
Author

tapir commented Jan 3, 2019

Unfortunately your example doesn't work for me at all. Not even in emulator. I've noticed you didn't the the workaround delay thing where RN-Webview bridge is not live yet. But even then it doesn't fire up a message for me. Here is how I've tried to run it.

import React, {Component} from 'react';
import {WebView} from 'react-native-webview';

const page = `
<html>
<head>
<script>
	(function() {
		window.WebViewBridge = {
			onMessage: function() {
				return null;
			},
			send: function(data) {
				window.postMessage(data, '*');
			},
		};
		var event = new Event('WebViewBridge');
		window.dispatchEvent(event);
	})();
	document.addEventListener('DOMContentLoaded', function(event) {
		try {
			window.WebViewBridge.send('test-data');
		}
		catch(err) {
			document.getElementById('errr').innerHTML = err.message;
		}
	});
</script>
</head>
<body>
	<div id='errr'>...</div>
</body>
</html>`;

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
		<WebView
			onMessage = {(e) => console.warn(e.nativeEvent.data)}
			source = {{html: page}}
		/>
    );
  }
}

@Titozzz
Copy link
Collaborator

Titozzz commented Jan 3, 2019

I do, just did not post it.

const outgoingWebViewMessageQueue = queue({
  concurrency: 0,
  autostart: false,
});

let queueShouldStart = false;

export const sendMessageToWebViewBridge = message => {
  if (isClientSide()) {
    outgoingWebViewMessageQueue.push(() => {
      window.WebViewBridge.send(message);
    });
    if (queueShouldStart) {
      outgoingWebViewMessageQueue.start();
    }
  }
};

and receiveMessageFromWebViewBridge is my custom message handler for the web

const setupWebViewBridge = () => {
  queueShouldStart = true;
  outgoingWebViewMessageQueue.start();
  window.WebViewBridge.onMessage = receiveMessageFromWebViewBridge;
};

export const initializeWebViewBridge = () => {
  if (isClientSide()) {
    if (window.WebViewBridge) {
      setupWebViewBridge();
    } else {
      window.addEventListener('WebViewBridge', () => {
        setupWebViewBridge();
      });
    }
  }
};

@tapir
Copy link
Author

tapir commented Jan 3, 2019

Had to use react-native-webview-bridge for now which is using an older version of WebView.

Hoping this gets fixed soon. Maybe a common working client side JS should be injected by default before everything else. Leaving this open. Let me know if you want me to test something.

@jamonholmgren jamonholmgren added bug and removed User: question Further information is requested labels Jan 5, 2019
@jamonholmgren
Copy link
Member

@tapir I've replicated the problem using your steps as well. Made this a bug.

If anyone wants to tackle it, there's a good replication step posted above.

@jamonholmgren jamonholmgren added the Help wanted Extra attention is needed label Jan 5, 2019
@Titozzz
Copy link
Collaborator

Titozzz commented Jan 7, 2019

Well we are already working on postMessage implementation, as we now the current implementation is really bad. #104

@abendi
Copy link

abendi commented Jan 21, 2019

Could it be that if(postMessage.length === 1) { ... } breaks on newer Chrome versions and that your real device and emulator have different Chrome versions?
see: facebook/react-native#11594 (comment)

@tapir
Copy link
Author

tapir commented Jan 23, 2019

Could it be that if(postMessage.length === 1) { ... } breaks on newer Chrome versions and that your real device and emulator have different Chrome versions?
see: facebook/react-native#11594 (comment)

tried with postMessage.toString().includes('__REACT_WEB_VIEW_BRIDGE') still doesn't work

@abendi
Copy link

abendi commented Jan 24, 2019

You can also try this:

window.onload = function() { 
  setTimeout(function() {
    window.parent.postMessage('test-data-as-string', '*');
  }, 2000);
};

This always waits for 2 seconds after the page has loaded.

@tapir
Copy link
Author

tapir commented Jan 24, 2019

How does that help? Initial example waits as much as necessary. It's recursive.

@abendi
Copy link

abendi commented Jan 24, 2019

They are not the same, because some Chrome versions might report postMessage.length === 1 falsely. On those versions the callback in recursive example gets called immediately when in reality the postMessage bridge is not actually ready.
Last example waits a constant time and does not rely on versioning. Try it out to exclude that case.

@tapir
Copy link
Author

tapir commented Jan 24, 2019

That why I've tried postMessage.toString().includes('__REACT_WEB_VIEW_BRIDGE') instead of postMessage.length === 1 like you recommended.
I'll try it without any tests as well but I doubt it will work.

@tapir
Copy link
Author

tapir commented Jan 24, 2019

Didn't work unfortunately

@Titozzz Titozzz closed this as completed Feb 1, 2019
@Titozzz

This comment has been minimized.

Titozzz added a commit that referenced this issue Feb 1, 2019
…entation (#303)

fixes #29
fixes #272
fixes #221
fixes #105
fixes #66

BREAKING CHANGE: Communication from webview to react-native has been completely rewritten. React-native-webview will not use or override window.postMessage anymore. Reasons behind these changes can be found throughout so many issues that it made sense to go that way.

Instead of using window.postMessage(data, *), please now use window.ReactNativeWebView.postMessage(data).

Side note: if you wish to keep compatibility with the old version when you upgrade, you can use the injectedJavascript prop to do that:

const injectedJavascript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

Huge thanks to @jordansexton and @KoenLav!
Titozzz pushed a commit that referenced this issue Feb 1, 2019
# [5.0.0](v4.1.0...v5.0.0) (2019-02-01)

### Features

* **Android/iOS postMessage:** refactoring the old postMessage implementation ([#303](#303)) ([f3bdab5](f3bdab5)), closes [#29](#29) [#272](#272) [#221](#221) [#105](#105) [#66](#66)

### BREAKING CHANGES

* **Android/iOS postMessage:** Communication from webview to react-native has been completely rewritten. React-native-webview will not use or override window.postMessage anymore. Reasons behind these changes can be found throughout so many issues that it made sense to go that way.

Instead of using window.postMessage(data, *), please now use window.ReactNativeWebView.postMessage(data).

Side note: if you wish to keep compatibility with the old version when you upgrade, you can use the injectedJavascript prop to do that:

const injectedJavascript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

Huge thanks to @jordansexton and @KoenLav!
@Titozzz
Copy link
Collaborator

Titozzz commented Feb 1, 2019

🎉 This issue has been resolved in version 5.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@tapir
Copy link
Author

tapir commented Feb 1, 2019

Works like a charm!

@koofka
Copy link

koofka commented Feb 1, 2019

@tapir Any chance you could share your generateMessage function? We are currently running into the same issue where it works perfectly in the simulator but not on the phone deploys.

@tapir
Copy link
Author

tapir commented Feb 1, 2019

@koofka I'm directly using the window.ReactNativeWebview.postMessage(strData) method. Nothing else. No timeouts nothing.
Are you sure you're on version 5.0.0? Also notice the small letter 'v' in 'Webview'. It's been wrongly documented as window.ReactNativeWebView.postMessage but later was fixed.

@koofka
Copy link

koofka commented Feb 1, 2019

@tapir Yep we added those fixes, Im wondering about the other direction for messaging into the webview. The only code that has worked for that so far for us has been :

 generateOnMessageFunction = (action, payload) =>
    `(function() {
      var event = new CustomEvent('WebViewBridge', {detail: {
          action: ${JSON.stringify(action)},
          payload: ${JSON.stringify(payload)},
        }
      });
      window.dispatchEvent(event);
    })()`
`

and then a corresponding window listener in the webview

@tapir
Copy link
Author

tapir commented Feb 1, 2019

Ah sorry, I've used it like this in my app

sendToWebview(msg) {
	const { webview } = this.refs;
	webview.postMessage(msg)
};
...
...
<WebView
	ref = "webview" // you need this for above to work
	...
	...
	source = {{uri: this.uri}}/>

and like below in the html served by webview

document.addEventListener('message', function (event) {
	// do your thing here with event.data
});

@koofka
Copy link

koofka commented Feb 1, 2019

interesting. Thanks for the help will give it a shot.

phuongwd pushed a commit to phuongwd/react-native-webview that referenced this issue Apr 29, 2020
…entation (react-native-webview#303)

fixes react-native-webview#29
fixes react-native-webview#272
fixes react-native-webview#221
fixes react-native-webview#105
fixes react-native-webview#66

BREAKING CHANGE: Communication from webview to react-native has been completely rewritten. React-native-webview will not use or override window.postMessage anymore. Reasons behind these changes can be found throughout so many issues that it made sense to go that way.

Instead of using window.postMessage(data, *), please now use window.ReactNativeWebView.postMessage(data).

Side note: if you wish to keep compatibility with the old version when you upgrade, you can use the injectedJavascript prop to do that:

const injectedJavascript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

Huge thanks to @jordansexton and @KoenLav!
phuongwd pushed a commit to phuongwd/react-native-webview that referenced this issue Apr 29, 2020
# [5.0.0](react-native-webview/react-native-webview@v4.1.0...v5.0.0) (2019-02-01)

### Features

* **Android/iOS postMessage:** refactoring the old postMessage implementation ([react-native-webview#303](react-native-webview#303)) ([f3bdab5](react-native-webview@f3bdab5)), closes [react-native-webview#29](react-native-webview#29) [react-native-webview#272](react-native-webview#272) [react-native-webview#221](react-native-webview#221) [react-native-webview#105](react-native-webview#105) [react-native-webview#66](react-native-webview#66)

### BREAKING CHANGES

* **Android/iOS postMessage:** Communication from webview to react-native has been completely rewritten. React-native-webview will not use or override window.postMessage anymore. Reasons behind these changes can be found throughout so many issues that it made sense to go that way.

Instead of using window.postMessage(data, *), please now use window.ReactNativeWebView.postMessage(data).

Side note: if you wish to keep compatibility with the old version when you upgrade, you can use the injectedJavascript prop to do that:

const injectedJavascript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

Huge thanks to @jordansexton and @KoenLav!
jaynilson added a commit to jaynilson/reactNative_service_USA that referenced this issue Sep 11, 2024
# [5.0.0](react-native-webview/react-native-webview@v4.1.0...v5.0.0) (2019-02-01)

### Features

* **Android/iOS postMessage:** refactoring the old postMessage implementation ([#303](react-native-webview/react-native-webview#303)) ([f3bdab5](react-native-webview/react-native-webview@f3bdab5)), closes [#29](react-native-webview/react-native-webview#29) [#272](react-native-webview/react-native-webview#272) [#221](react-native-webview/react-native-webview#221) [#105](react-native-webview/react-native-webview#105) [#66](react-native-webview/react-native-webview#66)

### BREAKING CHANGES

* **Android/iOS postMessage:** Communication from webview to react-native has been completely rewritten. React-native-webview will not use or override window.postMessage anymore. Reasons behind these changes can be found throughout so many issues that it made sense to go that way.

Instead of using window.postMessage(data, *), please now use window.ReactNativeWebView.postMessage(data).

Side note: if you wish to keep compatibility with the old version when you upgrade, you can use the injectedJavascript prop to do that:

const injectedJavascript = `(function() {
  window.postMessage = function(data) {
    window.ReactNativeWebView.postMessage(data);
  };
})()`;

Huge thanks to @jordansexton and @KoenLav!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help wanted Extra attention is needed Type: bug report
Projects
None yet
Development

No branches or pull requests

5 participants