Internal Chromium postMessage event - javascript

I have an application (Vanilla JS / Weback + Babel) that uses postMessage to communicate between host page and an iframe (different domains).
I have noticed, that on IOS 12 Chrome browser does send an event that lands in my listener on interactions like button click.
Event data looks like that:
{
"type":"org.chromium.contextMenuMessage",
"requestId":"CC0C490584C989ACE524F0ED69F418DD",
"x":245,
"y":399.5
}
What is it? I couldn't find any resource that could explain that behaviour.
Edit: also, the strangest thing about this event is, that it seems to be present on production environment, but not on localhost.

I'm seeing a very similar thing on my app. I'm receiving messages into a cross-domain iframe from Chrome on iOS v12 and Chrome v76.0.3809. The content of the message I'm receiving is different, more in the form of:
{
iv: e8LCrn94cSbycO3i
payload: daPXeVR5GBg2IffLQ/2fuTmVGzJLnM++z2nC+cjR5kGcG1VhIBHni6tIrw94Gg+KvyMUMVNY3mjfJ9jMhT4g8fcnngZkiLirqoUOqMagVY3gaEzYBCb4ZVgtRelv/paccs7hc/rMi+cDr2eCOSzzKQ6fpHU=
target_frame_id: 419b482d9b6c4565f8dd4e2f470a518
type: org.chromium.encryptedMessage
}
I'm not sure what Chrome is using this data for specifically, it's most likely for communicating with add-ons.
The salient point here is that if you're listening for messages in your iframe using something like:
window.addEventListener('message', callback);
then you will receive all messages sent via postMessage and it's up to you to filter them. Any code on the page, including the browser, has the ability to send message events.

It only triggers a message cross-domain. If the parent and child are on the same domain it doesn't happen. At least that's what I've found and, sadly, I'm stuck with finding more info as well.

Related

Javascript: Querying clipboard permissions on Firefox does not work

I'm trying to modify the content of the clipboard by executing the "copy" command on a focused DOM element. However, the new content comes from the server, and arrives from a websocket, which is then processed in a callback that does not come from direct user interaction.
Because it wasn't triggered by the user, it is not allowed to do such thing as modifying the clipboard content, as specified in the Firefox's MDM website . The error message is:
document.execCommand(‘cut’/‘copy’) was denied because it was not
called from inside a short running user-generated event handler.
To overcome this issue, the same page suggest to request permissions to the browser throught navigator.permissions.query():
navigator.permissions.query({name: "clipboard-write"}).then(result => {
if (result.state == "granted" || result.state == "prompt") {
/* write to the clipboard now */
}
});
However, thought the article they use different names for the permissions:
clipboard-write
clipboard-read
clipboardWrite
clipboardRead
Within the same site, the permissions article shows a browser compatibility table , where it says Firefox supports clipboardWrite from version 51 and clipboardRead from version 54.
The problem is that none of these permissions is working on Firefox (I'm using Firefox 63). The query callback is never called. I have tried the four permission names without any luck.
To make sure the mechanism was working, I tested other permissions, such as notifications which worked flawlessly (It showed "prompt")
navigator.permissions.query({name: "notifications"}).then(result => {
alert(result.state)
});
So my question is: Am I doing something wrong when requesting the permissions, or have this permissions changed whatsoever?
It is intentional, aka. by design in Firefox. They chose to not expose this API to WEB content, only for browser extensions.
See here and more specifically here for your reference:
We do not intend to expose the ability to read from the clipboard (the Clipboard.read and Clipboard.readText APIs) to the web at this time. These APIs will only be usable by extensions...

Inconsistent execution of executeScript in Cordova inAppBrowser on iOS

I'm having some trouble with some inAppBrowser behavior in my cordova app. Here's the code:
var codePass = fooCode;
var executeScriptFunc = function(event) {
ref.executeScript({
code: codePass
}, function (value) {});
ref.removeEventListener('loadstop', executeScriptFunc);
};
var ref = cordova.InAppBrowser.open(fooObject.link, "_blank", "location=yes,enableViewportScale=yes");
ref.addEventListener('loadstop', executeScriptFunc)
The strange thing here is that the code works perfectly every time when emulated. It opens the browser and executes the script no problem. But when I try it on my actual iPhone device, it doesn't always work. The script executes maybe every other time. But it's never even that consistent.
Both the emulator and iPhone are using iOS 9.3.4. Any ideas?
If the site inside the inAppBrowser happens to be served via HTTPS, the callback for executeScript() will not work if the site employs a Content-Security-Policy HTTP response header that does not contain the gap: or gap-iab: schemes for the default-src directive. These are needed because execution of the callback function on iOS relies on an iframe that gets added to the page.
You can check whether this is the root cause for the problem by opening Safari’s Web Inspector for the inAppBrowser—it has a separate Web Inspector instance, independent of the parent application that opened it—and look out for a corresponding error message in the console. Note that you should open the console before executeScript() is run, or otherwise you might not get the error message.
Make sure also that you don't have other event handlers firing at the same time during your polling.
I had multiple pollers all firing every second and that's when I ran into the issue.
After changing the polling time so they all fired at different times, the issue went away.

JavaScript problems when launching site in iframe

I have set up an Articulate Storyline course (a Flash version accessed using the page "story.html" and an HTML5 version accessed using "story_html5.html"). It works fine when run directly, however, when I try to run everything in an iframe on the company server (linking to the course files on my personal server) I get JavaScript errors:
The course uses player.GetVar("HTML5spelaren") to access a variable called HTML5spelaren, which is located on the story_html5.html page itself. When running in an iframe I get a "Permission denied to access property 'HTML5spelaren'".
Finally the course uses the JavaScript var newWin=document.window.open("report.html", "Kursintyg"); to display a course completion certificate in a new window. When running in an iframe however this results in a "Permission denied to access property 'open'".
Is there a way to rewrite the JavaScripts to get around this? I need to be able to detect if the course is running in Flash or HTML5 mode (that's what I use the variable in story_html5.html for), as well as being able to use JavaScript to open a new page from within the iframe when clicking on a link.
Page structure:
https://dl.dropboxusercontent.com/u/11131031/pagestructure.png
/Andreas
There's a way for different domains to speak to one another via javascript. You can use postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
In your case, in story.html or story_html5.html could use something like:
parent.postMessage(HTML5spelaren, parent_domain);
and you add an event listener in the company page:
window.addEventListener("message", receiveMessage, false);
And in receiveMessage function you retrieve the data that you need. Something like:
function receiveMessage(event){
your_variable = event.data
}
Same logic can be probably be applied to your popup.
You can post from child to parent or from parent to child.
My guess is that content you're linking to in the iFrame is on a different server/domain. If so, the error is a security feature to stop cross-site scripting (XSS) attacks.
Consider putting both the parent iFrame and the articulate content (child) on the same server. This should eliminate the problem.

Chrome App messaging and 'loadstop' vs. .load() timing

I am developing a Chrome App with webviews. Pages intended for the webviews may also run in a regular browser. If inside a webview, pages send messages to the main App, but apparently they need to get a message from the App first, in order to know where to send their messages.
No problem - the main App sends a message as soon as it sees a 'loadstop' event which tells the pages where to send messages to. If a page is not in a webview then it never gets the message.
The problem is, I need to know when a page should stop waiting for the message and assume it is NOT in a webview.
When does 'loadstop' occur, relative to events in the page such as jQuery's .ready or .load? Is there a way to trap or trigger an event guaranteed to occur after 'loadstop' MIGHT be seen in the main App and a message sent and received by the webview's JavaScript.
When does 'loadstop' occur, relative to events in the page such as jQuery's .ready or .load?
According to the documentation for the loadstop event:
Fired when all frame-level loads in a guest page (including all its subframes) have completed. This includes navigation within the current document as well as subframe document-level loads, but does not include asynchronous resource loads.
This would suggest it's more akin to jQuery's .ready(), which executes after the DOM tree is loaded, but before waiting for asset (.css, .js) downloads.
Keep an eye on that documentation page; it's already much improved since two weeks ago.
Is there a way to trap or trigger an event guaranteed to occur after 'loadstop' MIGHT be seen in the main App and a message sent and received by the webview's JavaScript?
Your manifest.json declares your my-app-main.js background script (and your webview permission) which launches your my-webview-wrapper.html which includes your <webview> tag and also inlines some javascript (or sources a my-webview-wrapper.js file) that assigns event listeners to your webview via an onload function as such:
onload = function() {
webview = document.getElementById("the-id-attribute-of-my-webview");
webview.addEventListener("<EVENT>", function() {
// the cool stuff you want to do
}
}
<EVENT> can be any of the webview DOM events listed in the documentation I linked (including loadstop). Your main app shouldn't really care that any of this is happening. (It's async! It's javascript! It's magic!)
If you're still confused, just poke around Google's webview sample on GitHub.

Event triggering from iframe

I'm integrating a third party photo upload service with my app. So I'm loading it in my page via iframe.
When the upload service is done with uploading my photo it can either trigger certain event to my parent page i.e :
parent.$('body').trigger('photoUpload.complete');
or it triggers a function in the parent page i.e :
window.parent.reloadParentPage();
In any case I get this warning in my chrome console :
Uncaught SecurityError: Blocked a frame with origin "https://photoupload.com" from accessing a frame with origin "https://website.com".
I realize this is a security issue as described here :
http://www.w3.org/TR/2008/WD-access-control-20080912/
So I wanted to enable the origin https://photoupload.com to access my site. I did this in my controller :
after_filter :set_access_control_headers
Then the method :
def set_access_control_headers
headers['Access-Control-Allow-Origin'] = "https://photoupload.com"
headers['Access-Control-Request-Method'] = '*'
end
Please not that https://photoupload.com is the photo upload service and https://website.com is my website. (Imaginary names for example sake), but they are both hosted on heroku.
How do I make this work?
Saw similar questions that people had success with this :
Triggering a jQuery event from iframe
Update
Maybe a better question would be, in which app should I set the headers? I was assuming in my app?
Update II
Is there a better way to do this? Send action/event/something from iframe to the parent page, so the parent page can react in some way
As long as you don't have to support IE6 or IE7, the preferred way to send cross-domain messages between an iframe and its parent is to use window.postMessage(...).
Since you have the ability to modify the upload service, you should have it invoke something like this:
window.parent.postMessage('photoUpload.complete', 'https://website.com');
(the second parameter can be set to '*' to allow the iframe to send messages regardless of the containing page's domain, but that's correspondingly less secure - may not be relevant in your case though as no actual data is being sent).
and your site would use something like
if (!window.addEventListener) {
// IE8 support (could also use an addEventListener shim)
window.attachEvent('onmessage', handleMessage);
} else {
window.addEventListener('message', handleMessage, false);
}
function handleMessage(event) {
// check where the message is coming from - may be overkill in your case
if (event.origin==='https://photoupload.org') {
// check event type - again probably not required
if (event.data==='photoUpload.complete') {
// do your thing
}
}
}
And if you want to send messages back from the outer page to the iframe, it's basically the same setup but you send with:
iframe.contentWindow.postMessage(...)
If IE7 or IE6 support is required, postMessage() is not supported but you could use something like http://easyxdm.net/wp/
I guess this line should work as well
window.parent.$(window.parent.document).trigger('photoUpload.complete');
Explanation: In your code parent.$('body').trigger('photoUpload.complete');
'body' is referring to iframe body and not the parent window body.

Categories