Skip to content

Switch to using Path URL Strategy #3585

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

Merged
merged 16 commits into from
Apr 26, 2022
Merged

Conversation

DanTup
Copy link
Contributor

@DanTup DanTup commented Jan 24, 2022

This changes the app to use the PageUrlStrategy in Flutter, which means URLs will just be standard like /foo instead of being added to the fragment like /#/foo.

@kenzieschmoll this seems to work well from my testing, including from VS Code (with a small tweak I'll ship in the next week or so), although I'm not certain there aren't pages with unusual navigation I may have missed.

Some things I'm not certain but should be checked before merging though"

  • It doesn't affect IntelliJ/Android Studio negatively (VS Code requires a change because it's over-encoding or URLs was causing issues here)
  • It doesn't affect anyone not using defaultHandler in the server... I've had to extend defaultHandler to be able to serve up index.html when a request comes in for something like /memory, because although navigations are usually done client-side, it's possible that the user hits Refresh or we try to launch directly on to a page which will now send /page_id to the server instead of just / which won't otherwise get the contents of index.html to launch the Flutter app.
  • Survey parsing may need updating

I'm not sure who best knows the answer to these questions though.

Fixes #2475.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 24, 2022

Oh also, survey parsing seems like it'll need updating - I'm not certain how to test that though. If you let me know I can ensure that's working correctly.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 24, 2022

@helin24 I don't know if this will affect IntelliJ/Android Studio. The reason VS Code needed a change was that the URLs were over-encoded which only seemed ok when we were using the fragment.

@kenzieschmoll
Copy link
Member

Generating the survey url does need updating. The value of fromPage here should reflect which page a user clicked the survey link from (e.g. '', 'performance', 'inspector', etc.).

You can force the survey prompt to come up by deleting the .devtools file (rm ~/.flutter-devtools/.devtools) and manually changing the date on your computer to a date between 2/19 and 3/19. You'll still need to build the release version of the app to see this since the survey logic requires the server.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 25, 2022

Done! I extracted the code that extracts the page to a non-web-specific file so it was easier to add tests for it - I don't know if analytics_common was the right place for it, let me know if it's better elsewhere.

Btw when I was testing this it worked fine running from the CLI on a new port, but when launching on the normal port from VS Code, the app seemed heavily cached and I had to use Chrome devtools to "clear application data" before the change took affect. I don't know if this is just because it's not a real release or if other users may see this.

@kenzieschmoll
Copy link
Member

I found an issue when connecting to an app from the connect screen. When doing this, the 'inspector' page does not show up in the url. This is not specific to the inspector, but rather the first page shown after connecting DevTools to the running app.

Repro steps:

  1. run devtools
  2. copy and paste a vm service uri for a running flutter app into the connect screen
  3. observe that 'inspector' is not present in the url. If you navigate to another page, and then click the inspector tab to go to the inspector page, you'll notice 'inspector' is now present.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 25, 2022

hmm, yeah - I can repro this. You can also just go direct to a URL like this:

http://127.0.0.1:9104/?uri=ws%3A%2F%2F127.0.0.1%3A59526%2FIRmSRy-94Gk%3D%2Fws

I think this is because we're just showing the first item in the tabbed control when there's no page, so it's valid to not have one and we'll show whatever is the first item.

If we never want this, I think probably we could check if there's a page in the URL after we connect, and if not, explicitly push the first visible screen?

@kenzieschmoll
Copy link
Member

I think probably we could check if there's a page in the URL after we connect, and if not, explicitly push the first visible screen

Yeah, once we show a tab, we should verify that the page name is in the url, and if not, we should add it.

@@ -97,7 +100,18 @@ Future<shelf.Handler> defaultHandler(
if (debugMode) {
return debugProxyHandler!(request);
} else {
return buildDirHandler!(request);
// If the user hits Refresh when on a page being handled by the Flutter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to this file will need to be applied to the g3 handler as well, but I have verified that things work as expected when that is done. I'll update the g3 handler when this change rolls into g3.


void main() async {
usePathUrlStrategy();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will need to be applied to the google3 main file. I'll take an action item to do this when this change is rolled into g3.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 26, 2022

I've pushed changes that should fix the URL thing. One updates the URL when the tab controller is initialised (so we know which first tab is being rendered if there isn't one in the QS), and one removes some code that had a bug if you went to /?page=memory then tried to navigate to another page, it was a bit confused about the current page. (Ultimately support for page= should probably be dropped, but IDEs are currently still using it).

@kenzieschmoll
Copy link
Member

Ultimately support for page= should probably be dropped, but IDEs are currently still using it

@DanTup @helin24 can we transition IDEs off of this pattern?

Copy link
Member

@kenzieschmoll kenzieschmoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm once tests pass

@helin24
Copy link
Member

helin24 commented Jan 26, 2022

Ultimately support for page= should probably be dropped, but IDEs are currently still using it

@DanTup @helin24 can we transition IDEs off of this pattern?

This would mean instead of ?page=inspector /inspector?query=etc? Should be pretty easy for IntelliJ

@DanTup
Copy link
Contributor Author

DanTup commented Jan 26, 2022

can we transition IDEs off of this pattern?

This would mean instead of ?page=inspector /inspector?query=etc? Should be pretty easy for IntelliJ

Yep, though we'll need to gate it on a version of DevTools that includes this PR (without the serveStaticFile change in this PR, the users would see 404s).

@DanTup
Copy link
Contributor Author

DanTup commented Jan 27, 2022

@kenzieschmoll bots are passing, but only because I added a Future.microtask(() { around the navigation. It seems not valid to navigate during initState (or didChangeDependencies) - which kinda makes sense, it's odd that we're "navigating" (even if we are doing Router.neglect) as we're building (we can't do it sooner because we don't know which tab will be the "default" one).

Do you know if there's a better way to do this?

Future.microtask(() {
final routerDelegate = DevToolsRouterDelegate.of(context);
Router.neglect(context, () {
routerDelegate.navigateIfNotCurrent(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using Future.microtask(), what about WidgetsBinding.instance.addPostFrameCallback ?

another idea - is there a better way to update the url that doesn't trigger a build in the first place?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another idea - is there a better way to update the url that doesn't trigger a build in the first place?

I haven't been able to find one. I thought what I was doing here with Router.neglect would do it, but even if it's not rebuilding, it does still throw being called here.

Instead of using Future.microtask(), what about WidgetsBinding.instance.addPostFrameCallback?

That seems to work (both in the browser, and the tests) so I've pushed that. I'm not sure it's an ideal fix, but I don't have any other ideas right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is forcing build to be called because routerDelegate.navigateIfNotCurrent eventually calls notifyListeners. One idea is to hide the notifyListeners call behind a bool if (shouldNotify). shouldNotify could be an optional named parameter that defaults to true. I haven't tried this locally, so we'll need to make sure that this doesn't break any routing behavior if notifyListeners was false. This may cause more trouble than it is worth, but could be worth looking into.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that notifyListeners() is how we notify Flutter that the URL may have changed (and in turn, it may update the visible URL to the user). If we remove that, I think the visible URL in the browser won't change at all (since we've only updated our own local state variables).

(If this doesn't seem right, I can do some testing of it though)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. The postFrameCallback seems fine then.

@@ -10,6 +10,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<base href="/">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elliette wanted to get your input on changes to this file to verify that this shouldn't have any effects on serving DevTools in g3 or with DDR / the Dart DevTools extension.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks safe to me! None of the serving logic should care about the base URL.

Copy link
Member

@kenzieschmoll kenzieschmoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for these changes, Danny. Can you please mirror the devtools_server/ changes in this PR to the new home of devtools_server inside dds?

external_handlers.dart --> https://github.com/dart-lang/sdk/blob/main/pkg/dds/lib/src/devtools/handler.dart#L26

server.dart --> https://github.com/dart-lang/sdk/blob/d1baf327a18dfc92476049a09985b645c08072f5/pkg/dds/lib/devtools_server.dart#L278

Please also leave the changes here for now, as we haven't completely migrated all code off of package:devtools_server and over to the dds implementation.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 31, 2022

Thanks! I want to re-test with these final tweaks in VS Code, and @helin24 may want to do the same for IntelliJ. If both are good I'll open the same in DDS.

@DanTup
Copy link
Contributor Author

DanTup commented Jan 31, 2022

This still works in VS Code. I'll make a start on the DDS PR tomorrow. @helin24 I don't anticipate the changes since you last tested (which I believe is just wrapping the router call in addPostFrameCallback) but if you want to test it before we merge, let me know.

@helin24
Copy link
Member

helin24 commented Jan 31, 2022

@DanTup we don't need to test again; thanks for checking

@DanTup
Copy link
Contributor Author

DanTup commented Feb 1, 2022

@kenzieschmoll

Thanks for these changes, Danny. Can you please mirror the devtools_server/ changes in this PR to the new home of devtools_server inside dds?

I see some code in here that seems to assume all paths will start with /devtools if notFoundHandler is not null. How is this currently used? I think in order to support this, the index.html would need to have <base href='/devtools' /> so that Flutters routing would update the URLs to the correct thing (/devtools/inspector).

If we don't expect the /devtools in the frontend paths, we'd need to know which urls (eg. out of /inspector and /foo) should serve up the static index.html and which should fall through to NotFoundHandler (which I think means having knowing a list of valid page URLs here).

@kenzieschmoll
Copy link
Member

CC @bkonyi ☝️

@DanTup DanTup force-pushed the url-path-strategy branch from bbcabd0 to 0aee389 Compare March 28, 2022 17:17
@DanTup
Copy link
Contributor Author

DanTup commented Mar 28, 2022

@kenzieschmoll

I've rebased this, and pushed DDS changes here:

https://dart-review.googlesource.com/c/sdk/+/239082/

This PR must not roll into the SDK without the changes above first landing, or DevTools will break. I do not know how g3 uses this and cannot test, but I've tested this by:

  • running ./tools/build.py --no-goma --mode release --arch x64 create_platform_sdk and testing dart devtools and `dart --observe foo.dart - this serves the existing (HashUrlStrategy) DevTools using the new handlers, and everything appears to work the same
  • Copying a built output of DevTools with this change into xcodebuild/ReleaseX64/dart-sdk/bin/resources/devtools and then testing both again - this serves the new (PathUrlStategry) DevTools and also appears to work, including navigating to the inspector and then hard-refreshing, which sends a request to the server for /inspector (or /authToken/devtools/inspector).

I concluded that serving always at /devtools and dropping the auth token would add some additional complexity (changing the URLs that IDEs are embedding, having to redirect from /#/inspector -> /devtools/inspector) and rewriting the base href would be simpler (although I don't think there's much in it so happy to change or discuss in more detail if you think we should do the other).

@DanTup
Copy link
Contributor Author

DanTup commented Apr 5, 2022

@kenzieschmoll @bkonyi I've not been able to track down why this change fails with old-style (fragment) URLs. It seems that at some point during initialisation (after switching to UrlPathStrategy, although exactly when seemed inconsistent even if I added lots of delays) the fragment was being dropped.

As a fix, I decided that the best thing would be to detect legacy URLs right at the start, and redirect (with location.replace) to the new-style equivalent. This also has the benefit that no part of the DevTools code will need to handle legacy URLs anymore, it's all handled just be one small mapping before initialisation.

I've pushed that change to this PR so they'll need reviewing.

Since it's been a while since this was started, a summary:

DDS Change

CL: https://dart-review.googlesource.com/c/sdk/+/239082/

  • Changes the server to rewrite the base href if present in the page so support the UrlPathStrategy version of DevTools
  • Serves up the contents of index.html for requests to URLs like /inspector so linking directly to those pages (or hitting refresh) will not result in a 404 from the server

I believe these changes are safe to land before the DevTools changes (the opposite is not true).

DevTools Change

This PR

  • Changes the app to use PathUrlStrategy to remove the # fragments in the URLs
  • Adds a base href to support the above
  • Redirects old legacy URLs to new URLs before initialising the app

These changes require the serve has the changes above to function correctly.

Testing

To test, I did:

  • build release version of DevTools
  • build SDK (./tools/build.py --no-goma --mode release --arch x64 create_platform_sdk)
  • Overwrite DevTools in third_party/devtools/web and sdk/xcodebuild/ReleaseX64/dart-sdk/bin/resources/devtools with the newly built DevTools

Things I've tested:

  • running xcodebuild/ReleaseX64/dart-sdk/bin/dart devtools serves a version using Url paths
  • running xcodebuild/ReleaseX64/dart-sdk/bin/dart --observe=0 /tmp/foo.dart prints a valid URL that loads DevTools (it redirects to remove the fragment, and then functions as expected)
  • running VS Code using xcodebuild/ReleaseX64/dart-sdk as the active SDK spawns a working embedded devtools
  • running VS Code using xcodebuild/ReleaseX64/dart-sdk as the active SDK spawns working external devtools pages, and can navigate between pages using the server API (by running commands in VS Code to launch those other pages)

Things I'm not able to test:

  • IntelliJ/Android Studio
  • Google3

Future Changes

After both changes are landed, further changes to be made:

  • Unskip the two DDS tests that are currently skipped because they test the served page has a base href
  • Remove the # from the URL printed by dart --observe as it's no longer necessary (although the redirect handles it so it doesn't cause any problems)

@DanTup
Copy link
Contributor Author

DanTup commented Apr 5, 2022

   info - test/url_utils_test.dart:36:69 - Missing a required trailing comma. - require_trailing_commas
   info - test/url_utils_test.dart:44:75 - Missing a required trailing comma. - require_trailing_commas

Pushed a fix for these, though for some reason I don't see these lints locally (nor can I see where require_trailing_commas is being enabled from), so if anyone has any ideas about that, I'm interested to know :-)

@kenzieschmoll
Copy link
Member

for some reason I don't see these lints locally

This lint was enabled in a recent PR. If you merge master you should see the lint warnings (you may have to restart the analysis server or your IDE if the change is not picked up automatically).

@DanTup
Copy link
Contributor Author

DanTup commented Apr 6, 2022

This lint was enabled in a recent PR. If you merge master you should see the lint warnings (you may have to restart the analysis server or your IDE if the change is not picked up automatically).

Ah, that's what it was. For some reason I thought GH Actions always ran the PR as it was on the source branch, but I can see from the logs that it merges master in first (which is better, I just didn't realise it did). Thanks!

@kenzieschmoll
Copy link
Member

Tested changes in google3. Google3 does not have a context where devtools is served under /devtools, so everything works as expected with a base href of /

@DanTup
Copy link
Contributor Author

DanTup commented Apr 26, 2022

@kenzieschmoll I merged master into this (there were some conflicts because url_utils was removed, but I'd added to it here, so this brings it back).

Do you need to do anything in g3 before landing this (the DDS change has landed)? I see #3585 (comment) but suspect that needs doing as it's rolled into g3?

@kenzieschmoll
Copy link
Member

Do you need to do anything in g3 before landing this (the DDS change has landed)?

Nope, I have a g3 CL ready to go that applies your changes to the g3 handler. You are free to land this.

/// This is a bit of a hack, as we have file paths instead of the explicit
/// "package:uris" we'd like to have. This will be problematic for a use case
/// such as "packages/my_package/src/utils/packages/flutter/".
String getSimplePackageUrl(String url) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove this method and associated tests? (essentially removing everything that was removed as part of https://github.com/flutter/devtools/pull/3932/files#diff-0fb55973e4befafb1d8f5ee26a619a6500f6524ef0dfcf2c252f62aa0644076f)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, good catch. I had to abort/re-do the merge and apparently didn't re-remove it the second time 😞

@DanTup DanTup merged commit 7dd8b98 into flutter:master Apr 26, 2022
@DanTup
Copy link
Contributor Author

DanTup commented Apr 26, 2022

@kenzieschmoll I did another final test and this all looked good, so I've landed it.

Anyone running the build_e2e.dart script will need to ensure they're on latest SDK to get the new server version for everything to work correctly.

Please ping me (and/or revert) if you think this has caused any serious issue. If you let me know once this version of the frontend has landed in the SDK, I'll un-skip those tests that rely on the base href (and tests again using the landed version of everything). Thanks!

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

Successfully merging this pull request may close these issues.

DevTools url has fragment and query parameters in the wrong order
5 participants