-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
[webview_flutter_wkwebview] Fixes JSON.stringify() cannot serialize cyclic structures #6274
[webview_flutter_wkwebview] Fixes JSON.stringify() cannot serialize cyclic structures #6274
Conversation
This is in wkwebview but maybe @ditman could review the js code? |
Can do, thanks for the mention @jmagman, I normally disregard webview stuff because I end up flagged as an owner (but I'm not :P) Ahem: |
The biggest problem I see with this is that this is a "lossy" fix; what you deserialize is not what you were serializing; (Also if the cycle is not important: maybe remove it before attempting to serialize to JSON?) ((PS: I don't think I've had to deal with JSON malformed like this before, but maybe with something like GraphQL you could end up with a circular dependency on your data, not sure...)) |
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.
LGTM
I didn't originally realize this was preventing an error from a method that was already a part of the script we were already injecting. I think this solution looks good.
cc @ditman or @stuartmorgan for secondary review.
Yea, I was considering this as well. But do think it is reasonable for a user to expect that |
Looking at the issue and the Android output, it looks like Android basically just calls String() on an object passed to the console. Android just returns |
Ah! I didn't realize this was to implement The way this works in the browser is by it not attempting to "toString" the whole thing, it just gives you a tree-like structure for the object: (You get tired of expanding The PR is a C+P from MDN however; @stuartmorgan licensing concerns? |
What is done in this PR is not permitted in Flutter. This code is not complicated enough to warrant us adding a |
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.
(Marking as changes needed to ensure this doesn't accidentally land as-is.)
@stuartmorgan Thanks for your reminder. I have adjusted the implementation to solve this issue by removing the cyclic object, please review the code again. |
Unless I'm missing something, the new version is destroying any duplicate objects, which is not the same thing as just removing cycles. |
…kwebview # Conflicts: # packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md # packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
@stuartmorgan I've re-adjusted the removal logic. |
cc |
const WKUserScript overrideScript = WKUserScript( | ||
''' | ||
function removeCyclicObject() { | ||
const levelObjects = []; |
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.
I'm not sure what this naming means; what is a "level object"?
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.
levelObjects
is used to record objects whose properties are currently being traversed. (Maybe I should rename it to stack
or something? )
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 levelObjects
is not equal, then obj1 will be popped up, and obj2 will be pushed to levelObjects.
const WKUserScript overrideScript = WKUserScript( | ||
''' | ||
function removeCyclicObject() { |
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.
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?
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.
@stuartmorgan At the expense of some performance this function could be defined inside of the log
function that calls it (line 659), and that way it wouldn't be directly exposed to the host page... But if we're not worried about a function log
colliding with the functions of a webpage, I wouldn't worry too much about removeCyclicObject
either :)
(In the engine we normally prefix with _
the name of the function so there's a fewer change of a global collision, but there's no guarantee that it won't collide. For a UUID you'd have to strip the -
characters for it to be a valid function name, but it could definitely work)
((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))
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.
At the expense of some performance this function could be defined inside of the
log
function that calls it (line 659), and that way it wouldn't be directly exposed to the host page
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.
But if we're not worried about a function
log
colliding with the functions of a webpage, I wouldn't worry too much aboutremoveCyclicObject
either :)
😬 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 _flutter_log_override
that's pretty unlikely to collide.
((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))
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.
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.
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.
Ah you're right, it's "free" then. _flutter_log_override
sounds like a good name. You can even have a small _flutter_webview_plugin_overrides
"namespace" object that contains all the methods that you're injecting, so it's easy to hide/move them around.
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.
// 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( |
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.
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.
LGTM with one naming suggestion.
} | ||
var _flutter_webview_plugin_overrides = _flutter_webview_plugin_overrides || { | ||
removeCyclicObject: function() { | ||
const levelObjects = []; |
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.
Based on the description in the PR comment, traversalStack
would probably be a clearer name.
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.
Thx.
…ialize cyclic structures (flutter/packages#6274)
…ialize cyclic structures (flutter/packages#6274)
flutter/packages@fd714bd...87a02e3 2024-05-15 engine-flutter-autoroll@skia.org Roll Flutter from d2da1b2 to 39651e8 (18 revisions) (flutter/packages#6738) 2024-05-15 stuartmorgan@google.com Update the repo for the 3.22 stable release (flutter/packages#6730) 2024-05-15 linxunfeng@yeah.net [webview_flutter_wkwebview] Fixes JSON.stringify() cannot serialize cyclic structures (flutter/packages#6274) 2024-05-14 louisehsu@google.com [in_app_purchase_storekit] migrate main plugin class to swift in preperation to storekit 2 (flutter/packages#6561) 2024-05-14 34871572+gmackall@users.noreply.github.com [image_picker_android] Refactor getting of paths from intent to single helper (flutter/packages#5009) 2024-05-14 engine-flutter-autoroll@skia.org Roll Flutter (stable) from 54e6646 to 5dcb86f (1402 revisions) (flutter/packages#6727) 2024-05-14 10687576+bparrishMines@users.noreply.github.com [webview_flutter_wkwebview] Skip `withWeakReferenceTo` integration test (flutter/packages#6731) 2024-05-14 engine-flutter-autoroll@skia.org Roll Flutter from 1255435 to d2da1b2 (26 revisions) (flutter/packages#6729) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC flutter-ecosystem@google.com,rmistry@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…yclic structures (flutter#6274) Using the `replacer` parameter of `JSON.stringify()` to remove cyclic object to resolve the following error. ``` TypeError: JSON.stringify cannot serialize cyclic structures. ``` ~~Related solution: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value~~ Fixes flutter/flutter#144535
…yclic structures (flutter#6274) Using the `replacer` parameter of `JSON.stringify()` to remove cyclic object to resolve the following error. ``` TypeError: JSON.stringify cannot serialize cyclic structures. ``` ~~Related solution: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value~~ Fixes flutter/flutter#144535
Using the
replacer
parameter ofJSON.stringify()
to remove cyclic object to resolve the following error.Related solution: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_valueFixes flutter/flutter#144535
Pre-launch Checklist
dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style.///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.