-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[webview_flutter_wkwebview] Fixes JSON.stringify() cannot serialize cyclic structures #6274
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
Changes from 7 commits
6057b1d
a37f7bf
182afac
ee66a4a
a709175
b7a3661
f7ac9bd
ae1a4a2
979d8ee
fd9bbcf
c32dedd
3700600
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -630,11 +630,34 @@ class WebKitWebViewController extends PlatformWebViewController { | |
| } | ||
|
|
||
| Future<void> _injectConsoleOverride() { | ||
| // Using the replacer parameter of JSON.stringify() to solve the error | ||
| // TypeError: JSON.stringify cannot serialize cyclic structures. | ||
| // See https://github.com/flutter/flutter/issues/144535. | ||
| // | ||
| // Considering this is just looking at the logs printed via console.log, | ||
| // the cyclic object is not important, so remove it. | ||
| const WKUserScript overrideScript = WKUserScript( | ||
| ''' | ||
| function removeCyclicObject() { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should have a name that cannot plausibly cause collisions on actual web pages, since it is injected into the page's context. E.g., maybe a Flutter prefix and a dynamically generated UUID? @ditman Any suggestions on best practice for avoiding naming collisions in JS?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stuartmorgan At the expense of some performance this function could be defined inside of the (In the engine we normally prefix with ((PS: Generating a UUID in JS: https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID, has just become "available" to Flutter web according to our Supported platforms))
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That sounds worth doing. If people are doing performance-affecting levels of print logging (and enabling this feature in release builds, which hopefully they aren't), they have problems already.
😬 I am very concerned about that, I just missed it somehow. We should absolutely rename that. Maybe UUID is overkill; we could also just do something like
We wouldn't need to do it in JS; we'd generate it in Dart, and then string interpolate that into the string we inject.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah you're right, it's "free" then.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| const levelObjects = []; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what this naming means; what is a "level object"?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As shown in the figure below, the properties of the outermost object (blue box) are currently being traversed, and obj1 (green box) is also an object, so the properties of obj1 will be traversed next and pushed in levelObjects.
When traversing all the properties of obj1 (green box) and starting to traverse obj2, currentParentObj will point to the outermost object (blue box) and the last element obj1 (green box) of the current
|
||
| return function (k, v) { | ||
| if (typeof v !== "object" || v === null) { return v; } | ||
| const currentParentObj = this; | ||
| while ( | ||
| levelObjects.length > 0 && | ||
| levelObjects[levelObjects.length - 1] !== currentParentObj | ||
| ) { | ||
| levelObjects.pop(); | ||
| } | ||
| if (levelObjects.includes(v)) { return; } | ||
| levelObjects.push(v); | ||
| return v; | ||
| }; | ||
| } | ||
|
|
||
| function log(type, args) { | ||
| var message = Object.values(args) | ||
| .map(v => typeof(v) === "undefined" ? "undefined" : typeof(v) === "object" ? JSON.stringify(v) : v.toString()) | ||
| .map(v => typeof(v) === "undefined" ? "undefined" : typeof(v) === "object" ? JSON.stringify(v, removeCyclicObject()) : v.toString()) | ||
| .map(v => v.substring(0, 3000)) // Limit msg to 3000 chars | ||
| .join(", "); | ||
|
|
||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment briefly explaining the approach at a high level so readers don't need to reverse-engineer what is being done here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.