MithrilJS / mithril.js

A JavaScript Framework for Building Brilliant Applications
https://mithril.js.org
MIT License
14.02k stars 925 forks source link

m.trust carsh in safari - ios 16.3 #2820

Closed 0ui0 closed 1 month ago

0ui0 commented 1 year ago

Mithril.js version: 2.2.2 Browser and OS: ios16.3 safari

Project: icat

The page is working normal in PC chrome, MAC sarari-16.3 (18614.4.6.1.5),Android chrome(unkown version) But it carshed in iphone safari-ios16.3

When loading this page, all touch actions will be slow, and after that, the page cahshed.

I debug and find it happend when I use m.trust() to parse the HTML from marked.js

crashed code:

 m.trust(format(self.content,self.contentType))

or

 m.trust(marked(markdownCode))

but not all page crashed, only the page, i review the page's markdown, it use normal grammer no special --- no extend HTML.

because it working nomal in other browser

I don't know it is whether or safari's bug or marked.js bug or mithril.js bug

0ui0 commented 1 year ago

maybe because m.trust include marilto link like this: abcdefg@abc.com

<a href=mailto:&#97;&#98;&#x63;&#x64;&#101;&#x66;&#103;&#x40;&#x61;&#98;&#x63;&#46;&#99;&#111;&#x6d;  target='_blank'>&#97;&#98;&#x63;&#x64;&#101;&#x66;&#103;&#x40;&#x61;&#98;&#x63;&#46;&#99;&#111;&#x6d;</a>
pygy commented 1 year ago

Could you share examples of the HTML source that crashes Safari? m.trust() uses innerHTML under the hood, no high magic...

Short of leaking memory from JS, a page shouldn't crash a browser, so it's likely a Safari bug.

It would be nice to see what markup causes it though (what tags, does it contain CSS, etc...).

0ui0 commented 1 year ago

Could you share examples of the HTML source that crashes Safari? m.trust() uses innerHTML under the hood, no high magic...

Short of leaking memory from JS, a page shouldn't crash a browser, so it's likely a Safari bug.

It would be nice to see what markup causes it though (what tags, does it contain CSS, etc...).

this is the page https://www.o-o.space/#!/home/479

I connected my iphone to the mac, and then copied the source code from the development tools of safari (this code is in my local environment, different from the web version)

But it is looks hard to read

I wanted to prepare a minimal version that only included the mithril component where the problem was, but when I isolated the part that I thought might be a bug(only include m.trust(...))

the page works!

but at crashed page, when I remove the m.trust(...),the page works

Too strange

I'll review and check the code again

maybe some other reasons

This code doesn't help the question
I delete the code
pygy commented 1 year ago

So you're passing a full HTML page to m.trust()?

Wouldn't you be better served by an m("iframe", {srcdoc: yourPageSource}) instead of m.trust(yourPageSource)?

0ui0 commented 1 year ago

https://www.o-o.space/#!/home/479

ohh,no no 😂 the m.trust only be used to parse markdown-html-code as before mentioned like:

<a href=mailto:&#97;&#98;&#x63;&#x64;&#101;&#x66;&#103;&#x40;&#x61;&#98;&#x63;&#46;&#99;&#111;&#x6d;  target='_blank'>&#97;&#98;&#x63;&#x64;&#101;&#x66;&#103;&#x40;&#x61;&#98;&#x63;&#46;&#99;&#111;&#x6d;</a>
m.trust(marked(/*article-start...*/"abcdefg@abc.com"/*article-end...*/))

the HTML-page is the content from safari -> look source code it rendered by m.mount(...)

i found some other situations:

I checked the code carefully and found that the problem is more complicated than I expected The following conditions must be met The safari page will not respond:

  1. The a tag containing mailto must appear in the component of mithril
  2. I use the listener touchstart\touchmove\touchend on my page to control the [pull-down refresh] at the top

In the callback function of these listeners, the m.redraw() function is used to control some changes of the page by modifying the state of some variables

The above two occur at the same time, and the click event running on the safari page will not work normally.

Sorry my previous description wasn't precise enough The problem page is not completely unresponsive The problem page can still be scrolled, and mouseover and mouseout can be triggered on the iphone (on the iphone) But all click events (whether click or touch) are failing

But this situation has not been reproduced under pc chrome, pc Firefox or mac safari, including the mobile development mode of pc chrome, and the chrome of Android phone.

If I use str.replace to remove the mailto tag, or I close the listener of [Pull-down Refresh], if one of them is satisfied, the problem of safari’s unresponsive touch will disappear

pygy commented 1 year ago

What I'd like to see is the exact input that goes into m.trust(). In other words the return value of marked(/*article-start...*/"abcdefg@abc.com"/*article-end...*/)

0ui0 commented 1 year ago

What I'd like to see is the exact input that goes into m.trust(). In other words the return value of marked(/*article-start...*/"abcdefg@abc.com"/*article-end...*/)

console.log(node = m.trust(marked(content)))

Object

attrs: undefined

children: "<p>title test<br><a href=mailto:&#x61;&#98;&#x63;&#x64;&#x65;&#102;&#103;&#64;&#97;&#98;&#x63;&#x2e;&#99;&#x6f;&#x6d;…"

dom: undefined

domSize: undefined

events: undefined

instance: undefined

key: undefined

state: undefined

tag: "<"

text: undefined

console.log(node.children)

<p>title test<br><a href=mailto:&#x61;&#98;&#x63;&#x64;&#x65;&#102;&#103;&#64;&#97;&#98;&#x63;&#x2e;&#99;&#x6f;&#x6d;  target='_blank'>&#x61;&#98;&#x63;&#x64;&#x65;&#102;&#103;&#64;&#97;&#98;&#x63;&#x2e;&#99;&#x6f;&#x6d;</a></p>

console.log(content)

title test
abcdefg@abc.com

console.log(marked(content))

<p>title test<br><a href=mailto:&#x61;&#98;&#x63;&#100;&#x65;&#x66;&#103;&#64;&#x61;&#98;&#x63;&#46;&#99;&#x6f;&#109;  target='_blank'>&#x61;&#98;&#x63;&#100;&#x65;&#x66;&#103;&#64;&#x61;&#98;&#x63;&#46;&#99;&#x6f;&#109;</a></p>
0ui0 commented 1 year ago

I use this as temporary solution:

content = content.replace(/([a-zA-Z0-9_]+)@([a-zA-Z0-9_\.]+)\.([a-zA-Z]+)/g,"$1# $2.$3")
pygy commented 1 year ago

Thanks, this looks completely inoccuous... I don't think Mithril or the HTML is at fault here. At least not the bits you shared. It could cause the planets to align just wrong and trigger a different bug though. Does desktop safari crash too?

If so, could you share the relevant output of the Console app that logs system events? There may be a way to get a remote event log from iOS devices too but I don't know how to do that.

0ui0 commented 1 year ago

Thanks, this looks completely inoccuous... I don't think Mithril or the HTML is at fault here. At least not the bits you shared. It could cause the planets to align just wrong and trigger a different bug though. Does desktop safari crash too?

If so, could you share the relevant output of the Console app that logs system events? There may be a way to get a remote event log from iOS devices too but I don't know how to do that.

Yeah, it only crashed in my ios safari -- and recently I update my iphone and ipad to ios 16.3. so i'm not certain it happend in other ios version. But in my Mac's safari, it can runs good.

so i hightly doubt that it is a safari's bug. Because I visit my website everyday, and before my update, I never found the situation. Tough I also update my program recently. But the changed code does not involve these modules.

I link my iphone to my macbook and use devTools check it due to some javascript Error at console, but nothing. I change to the performance tag, check for high cpu or memory usage, but the line is flat.

Because the iphone debugging page is very troublesome, I tried to split the code to single page see if it can be reproduced, but failed, I will continue to follow up and observe this situation

pygy commented 1 year ago

AFAIK, short of a bug, the only way to crash a browser tab is to exhaust memory.

I don't think that m.trust() can do that. m.trust() alone just creates a vnode. When that vnode is rendered, a temp DOM node is created, we set its innerHTML, then move the children from the temp node to the parent of the trusted vnode.

Does the m.trust() vnode have siblings? If not, you can replace the m.trust() vnode with a oncreate hook that sets innerHTML on the parent node... If the crash still happens, this would exonerate m.trust entirely.

0ui0 commented 1 year ago

AFAIK, short of a bug, the only way to crash a browser tab is to exhaust memory.

I don't think that m.trust() can do that. m.trust() alone just creates a vnode. When that vnode is rendered, a temp DOM node is created, we set its innerHTML, then move the children from the temp node to the parent of the trusted vnode.

Does the m.trust() vnode have siblings? If not, you can replace the m.trust() vnode with a oncreate hook that sets innerHTML on the parent node... If the crash still happens, this would exonerate m.trust entirely.

Oh, you may have missed my supplementary instructions. I mentioned that when the two points happen at the same time, it will cause a crash. Using m.trust alone or using touch events will not happen

supplementary instructions

pygy commented 1 year ago

Indeed I had skipped that bit of your post, sorry.

What if you add a mailto: link statically in the HTML source page? What about a m('a', {href:"mailto:..."}) link instead of the m.trust bit?

Edit: you can encode the email like this:

function htmlEncode(src) {
  return [...src].map(c => `&#${
    Math.random() < 0.5 ? c.codePointAt(0).toString() : "x" + c.codePointAt(0).toString(16)
  };`).join("")
}

marked uses a mix of decimal and hexadecimal html entities escapes.

0ui0 commented 1 year ago

Indeed I had skipped that bit of your post, sorry.

What if you add a mailto: link statically in the HTML source page? What about a m('a', {href:"mailto:..."}) link instead of the m.trust bit?

Edit: you can encode the email like this:

function htmlEncode(src) {
  return [...src].map(c => `&#${
    Math.random() < 0.5 ? c.codePointAt(0).toString() : "x" + c.codePointAt(0).toString(16)
  };`).join("")
}

marked uses a mix of decimal and hexadecimal html entities escapes.

I record a video : videoLink I don't know whether you can look if not, i will upload to another platform (:з」∠)

0ui0 commented 1 year ago

Indeed I had skipped that bit of your post, sorry.

What if you add a mailto: link statically in the HTML source page? What about a m('a', {href:"mailto:..."}) link instead of the m.trust bit?

Edit: you can encode the email like this:

function htmlEncode(src) {
  return [...src].map(c => `&#${
    Math.random() < 0.5 ? c.codePointAt(0).toString() : "x" + c.codePointAt(0).toString(16)
  };`).join("")
}

marked uses a mix of decimal and hexadecimal html entities escapes.

I try to upload to the google drive,but the speed is too slow....

pygy commented 1 year ago

I can see the video, I'll watch it later today.

0ui0 commented 1 year ago

I can see the video, I'll watch it later today.

when I log out i find it can only play one minutes, emmm let us wait google drive...

0ui0 commented 1 year ago

I can see the video, I'll watch it later today.

here is the google drive:link

JesseKoldewijn commented 1 year ago

Hey man, were you able to fix the issue at hand? @0ui0

JesseKoldewijn commented 1 year ago

Also something completely besides the point. Is there any specific reason for client-side rendering your fairly complex looking data intensive platform that you're building right now? Reason for asking is because of the slow load-times and I can imagine that this matter would improve a lot when server-rendering the UI and only loading the actual content on the client side from js.

Apart from that bit. The side you're building looks great, props to ya man! (even though I can't read any of it without google translate)

0ui0 commented 1 year ago

Also something completely besides the point. Is there any specific reason for client-side rendering your fairly complex looking data intensive platform that you're building right now? Reason for asking is because of the slow load-times and I can imagine that this matter would improve a lot when server-rendering the UI and only loading the actual content on the client side from js.

Apart from that bit. The side you're building looks great, props to ya man! (even though I can't read any of it without google translate)

Yeah, thanks for your advice and appreciation

now I only replace the @ sign as the video doing I doubt the part <a href=abc@abc.com....> no quote sign? or other reason let the page happen strange things my markd.js is old version, I don't know whether or not update it will solve the problem. But when i update, the latest version seems incompatible with vite. So I keep the present version.

Maybe I will spend more special time pay attention to this. But now, unless it happen chrome or firefox. I care about other things emmmm

--- about client-side rendering --- Because I hope it work on mobile application via webview as little migration work as possible and infact due to SEO, I prepare a simple server-side rendering version here URL. But when loading completely, the single page app will bring us better user experience then the traditional web page, and I organize mithril.js to building will be more easily and need not consider more situation. Tough the size of main.js certainly become more and more huge😂, and need more loading time. Maybe I will consider prepare a little easy game or other interaction way to help user spend the boring loading time...

--- about language --- Because the original idea was to want it to run at my country locally and without thinking about internationalization😂, so....

0ui0 commented 1 year ago

I can see the video, I'll watch it later today.

hey, i find this will lead to same problem today, also at ios safari: m.trust(<a href=mailto:${htmlEncode("abc@abc.com")}>${htmlEncode("abc@abc.com")}) and I copy the output of console.log(...these string...) and as input to m.trust, as i before do, the problem missing

so strange

dead-claudia commented 1 year ago

I can see the video, I'll watch it later today.

hey, i find this will lead to same problem today, also at ios safari:

m.trust(<a href=mailto:${htmlEncode("abc@abc.com")}>${htmlEncode("abc@abc.com")})

and I copy the output of console.log(...these string...) and as input to m.trust, as i before do, the problem missing

so strange

@0ui0

Just to verify: does this cause a crash?

`<a href=mailto:${htmlEncode("abc@abc.com")}>${htmlEncode("abc@abc.com")}</a>`

If not, does this?

var elem = document.createFragment("div")
elem.insertAdjacentHTML("afterbegin", `<a href=mailto:${htmlEncode("abc@abc.com")}>${htmlEncode("abc@abc.com")}</a>`)

If that doesn't either, does this?

var elem = document.createFragment("div")
elem.innerHTML = `<a href=mailto:${htmlEncode("abc@abc.com")}>${htmlEncode("abc@abc.com")}</a>`
dead-claudia commented 1 year ago

Oh, and can you make a full code sample of something that crashes on https://flems.io, https://codepen.io, or similar?

I'm typing this from an iPhone, so I definitely have a device to test it on. 🙂

0ui0 commented 1 year ago

Oh, and can you make a full code sample of something that crashes on https://flems.io, https://codepen.io, or similar?

I'm typing this from an iPhone, so I definitely have a device to test it on. 🙂

I find the key problem! when use m.redraw() in document.addEventListener("touchend") and Math.random() appear "href" of a Tag it crashed

look at the example, the button can't click:

https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0ALeBbKIANCAGYCWMYaA2qAHYCGuEamO+RIsA9nYn6wA8AQgAiAeQDCAFQCaABQCiAAnZQAfAB06gtcqgM6AcwC8mkBDrmtOrBAZIby5YObwGy2FgYAnSPDMQAFVpADEAWgAOa21nVwh3ZUZmQIA3MggAdwAHbh94c09efgDzTLIkeCwTJAh02Ahw8sqsQmUyOjJ4MgYocLBYXogTAEYMAAYYujju+Bh1MAYSXzJlRDB4QQB6WfntbW27BxtBACNuJABPJxcBnzJs+GUwH1hAra2AVzpsgGsjDA8XBbXBdLD3KAgsEQjAAKzA1m2dwe8BurgYHRucWRjyxzn0CWUuAx0xMyiQ3Fgn2YfAwAEdPhAfJcAMrQODwPIACnMxI65gAlLF8c4YE8Nj5lGTzDLpiLlMKRbgMLhuN94Fy+XRCMBFfLeLAfPZEFyBbq5fL8RSqTT4BgHEhFKlLPAADJkDaWJk8kCcz5eSxIcyEU0mdTmy2W5VGpA+BiZU16y0AX0IyygkCFVgtkaT+NTeec6SypojkecRvgnx801wPuDZfLzg2lxgqEbTecdjIRhwqHMI3GQ+yAA9ZZ3nMnC-nCNRp0qMPAfJ8NlyAAaCDzgiAkEwAcgAJMAALIMKoYON0Cl1gXJvfqEeP7YMdRrgWEef4uvmJBkVINz8RRbNsOwnZRsgdDojH7EBB1HYNAPlU4GFgf4fDVK8YIAYggXDxzA5QCxzTsDSgMhUNLRCRSGfIuQlLMCPxCUAGpRiowjEILLgyNQokIEFD9iMjb8QGDejBM7ABdBjIynITkxkuIthxVFhW2LUTi2c4rk0tR1E4SAYAQMheCodAACYRlQQcQFTegmBYdA4SoYgeD4F1WFsnUQGSRzMFgMAXK4YoPPQWzJOTIA

dead-claudia commented 1 year ago

Oh, and can you make a full code sample of something that crashes on https://flems.io, https://codepen.io, or similar?

I'm typing this from an iPhone, so I definitely have a device to test it on. 🙂

I find the key problem!

when use m.redraw() in document.addEventListener("touchend")

and Math.random() appear "href" of a Tag

it crashed

look at the example, the button can't click:

https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0ALeBbKIANCAGYCWMYaA2qAHYCGuEamO+RIsA9nYn6wA8AQgAiAeQDCAFQCaABQCiAAnZQAfAB06gtcqgM6AcwC8mkBDrmtOrBAZIby5YObwGy2FgYAnSPDMQAFVpADEAWgAOa21nVwh3ZUZmQIA3MggAdwAHbh94c09efgDzTLIkeCwTJAh02Ahw8sqsQmUyOjJ4MgYocLBYXogTAEYMAAYYujju+Bh1MAYSXzJlRDB4QQB6WfntbW27BxtBACNuJABPJxcBnzJs+GUwH1hAra2AVzpsgGsjDA8XBbXBdLD3KAgsEQjAAKzA1m2dwe8BurgYHRucWRjyxzn0CWUuAx0xMyiQ3Fgn2YfAwAEdPhAfJcAMrQODwPIACnMxI65gAlLF8c4YE8Nj5lGTzDLpiLlMKRbgMLhuN94Fy+XRCMBFfLeLAfPZEFyBbq5fL8RSqTT4BgHEhFKlLPAADJkDaWJk8kCcz5eSxIcyEU0mdTmy2W5VGpA+BiZU16y0AX0IyygkCFVgtkaT+NTeec6SypojkecRvgnx801wPuDZfLzg2lxgqEbTecdjIRhwqHMI3GQ+yAA9ZZ3nMnC-nCNRp0qMPAfJ8NlyAAaCDzgiAkEwAcgAJMAALIMKoYON0Cl1gXJvfqEeP7YMdRrgWEef4uvmJBkVINz8RRbNsOwnZRsgdDojH7EBB1HYNAPlU4GFgf4fDVK8YIAYggXDxzA5QCxzTsDSgMhUNLRCRSGfIuQlLMCPxCUAGpRiowjEILLgyNQokIEFD9iMjb8QGDejBM7ABdBjIynITkxkuIthxVFhW2LUTi2c4rk0tR1E4SAYAQMheCodAACYRlQQcQFTegmBYdA4SoYgeD4F1WFsnUQGSRzMFgMAXK4YoPPQWzJOTIA

This link is not crashing for me, unfortunately. I am seeing no click event, though, but I suspect that's actually a browser bug.

JesseKoldewijn commented 1 year ago

I don't see a triggered click on safari (iOS 17.1) either.