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

Feature request - implement a way to avoid clash with third party "message" events #3432

Closed
LeonSebastianCoimbra opened this issue Nov 4, 2021 · 4 comments · May be fixed by #3692
Closed
Labels

Comments

@LeonSebastianCoimbra
Copy link

LeonSebastianCoimbra commented Nov 4, 2021

This can happen when integrating ElFinder with third-party frameworks (or widgets) on which we have no control.

Imagine that this third party framework include (somewere in his code) something similar to this (javascript decompiled code):

		...	
		var n = "ej2" + function(e) {						// n = "ej23518,25636,63989,33834,63789", e = ƒ ()
			for (var t = "", r = 0; r < 5; r++)
				t += (r ? "," : "") + e[r];
			return t
		}(r)
		  , 
		  i = function(r) {
			r.source === window && "string" == typeof r.data && r.data.length <= 32 && r.data === n && (e(),
			t())
			};
			
		return window.addEventListener("message", i, !1),
		
		window.postMessage(n, "*"),
		
		t = function() {
			window.removeEventListener("message", i),
			e = i = n = void 0
		}
		...

This piece of code is functional to the other widgets of the framework, and send, as payload, a string composed with a prefix and a list of numbers (in the example "ej23518,25636,63989,33834,63789").

If the integration between ElFinder and the framework is of a certain type, the message is also managed by ElFinder in this place:

// bind window onmessage for CORS
$(window).on('message.' + namespace, function (e) {

	var res = e.originalEvent || null,
		obj, data;

	if (res &&
		(self.convAbsUrl(self.options.url).indexOf(res.origin) === 0 || self.convAbsUrl(self.uploadURL).indexOf(res.origin) === 0)) {

		try {
			obj = JSON.parse(res.data);
			data = obj.data || null;
			if (data) {
				if (data.error) {
					if (obj.bind) {
						self.trigger(obj.bind+'fail', data);
					}
					self.error(data.error);
				} else {
					data.warning && self.error(data.warning);
					self.updateCache(data);
					data.removed && data.removed.length && self.remove(data);
					data.added   && data.added.length   && self.add(data);
					data.changed && data.changed.length && self.change(data);
					if (obj.bind) {
						self.trigger(obj.bind, data);
						self.trigger(obj.bind+'done');
					}
					data.sync && self.sync();
				}
			}
		} catch (e) {
			self.sync();
		}
	}
});

The message is accepted because it satisfy the CORS condition, but when it tries to obtain an object from the payload, the "JSON.parse(res.data)" fails (because the payload is not a json object) and the code continue in the catch block. In the catch block, the execution of the "sync" method will cause ElFinder to reload its content, losing the current position on the tree.

Plus, we can not have full control on how many messages the third party framework will send and this will result in an unnecessary communication between the client and the server (continuous reload).

Since the problem is generic and very specific, my suggestion is to add a new option in options: a function (by default null) that if present will do a pre-evaluation of the payload and return true (if the message is for ElFinder) or false (if the message is not for ElFinder and, being so, it should not be managed).

As suggestion, the option could be:

	/**
	 * Called prior to manage a window.postMessage.
	 * 
	 * If not defined or defined and return true, the message will be managed by ElFinder. 
	 * If defined and return false, ElFinder will ignore the message.
	 * 
	 * This can be useful when third party widgets send messages that are intercepted by ElFinder
	 * with a payload (data) that is not managed by ElFinder: ElFinder expect a Json string and if
	 * the parseJson fails it will perform a sync, altering the content of the file manager.
	 * 
	 * @type Function
	 * @default null (ElFinder will manage the message)
	 */ 
	manageWindowMessage: null,

This option can then be used in the "$(window).on('message.'" as follow:

	// bind window onmessage for CORS
	$(window).on('message.' + namespace, function (e) {

		var res = e.originalEvent || null,
			obj, data;

		// Avoid to manage messages that are generated from third parties
		if (self.options.manageWindowMessage != null &&
			self.options.manageWindowMessage != "undefined" &&
			!self.options.manageWindowMessage(res.data)) return;

		if (res &&
			(self.convAbsUrl(self.options.url).indexOf(res.origin) === 0 || self.convAbsUrl(self.uploadURL).indexOf(res.origin) === 0)) {

I implemented this solution in my integration as follow:

var options = {
	...
	manageWindowMessage: function (data) {
		return !(data.startsWith('ej2'));
	},

And it is working fine.

If you find this solution reasonable, please include it in the next release of ElFinder. If you need, i can make the changes in a dedicated branch and then submit a pull request.

Thanks in advance for your attention.

ADDENDUM

I discovered that the following comment:

slice = /*@cc_on @if (@_jscript_version <= 5.8)
	function () {
		var a = [], i = this.length;
		while (i-- > 0) a[i] = this[i];
		return a;
	}@else@*/Array.prototype.slice/*@end@*/;

Can be problematic with some bundle tools.... the "@if" is apparently confused as a C# directive and can generate errors during the bundle process. If it is not important to leave this comment where it is, it can be useful to remove it.

@github-actions
Copy link

github-actions bot commented Jul 9, 2023

This issue is stale because it has been open for 50 days with no activity.

@github-actions github-actions bot added the stale label Jul 9, 2023
@github-actions
Copy link

This issue was closed because it has been inactive for 14 days since being marked as stale.

@blutorange
Copy link
Contributor

blutorange commented Nov 28, 2024

Would be great if this could be reopened, as this is still an issue. For example, I was trying to integrate monaco editor with elFinder, which also uses messages to communicate.

But my suggestion would be to always include some elFinder specific piece of data in the payload, such as "type": "io.studio-42.github" and ignore any messages that do not match (or that are not valid JSON)

@blutorange
Copy link
Contributor

blutorange commented Nov 28, 2024

Also, what is that message listener for? I didn't find any place in the source where it triggers such an event, and when setting a breakpoint via the dev tools, I also didn't find any situation where it got triggered.

Edit: The post message seems to happen on the PHP side where it creates a piece of JavaScript code that posts the message. The easiest solution as mentioned above I think would be to e.g. to add type property and check for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants