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

Added local directory listing feature. #32167

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

Bobulous
Copy link

@Bobulous Bobulous commented Apr 27, 2024

Added code to Servo to generate a local directory listing when visiting local directory paths.


  • There are tests for these changes OR
  • These changes do not require tests because ___

@mrobinson
Copy link
Member

It looks like the test is failing:

 failures:

---- fetch::test_local_directory_listing stdout ----
thread 'fetch::test_local_directory_listing' panicked at components/net/tests/fetch.rs:402:13:
assertion `left == right` failed
  left: "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>Directory listing: /home/runner/work/servo/servo/tests/wpt/directory-listing-example</title>\n    <style>\n        h1 {\n            text-align: center;\n        }\n        table {\n            font-family: monospace;\n            margin: 1em auto;\n            border-spacing: 0.2em 0.6em;\n            border-collapse: separate;\n            border: 1px solid black;\n            text-align: center;\n        }\n        th[scope=col] {\n            background-color: #ddd;\n            border: 1px solid #bbb;\n            padding: 0.2em;\n        }\n        th, td {\n            border: 1px solid #ddd;\n            padding: 0.2em;\n        }\n        td.numeric {\n            text-align: right;\n        }\n        footer {\n            margin-top: 1em;\n            border-top: 1px solid gray;\n            font-size: small;\n            color: gray;\n            text-align: right;\n        }\n    </style>\n</head>\n<body><h1>Index of /home/runner/work/servo/servo/tests/wpt/directory-listing-example</h1><table><thead><tr><th scope=\"col\">Type</th><th scope=\"col\">Name</th>\n<th scope=\"col\">Size</th></tr></thead><tbody><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"README.txt\">README.txt</a></td><td class=\"numeric\">375 B</td></tr><tr><td><abbr title=\"symlink\">S</abbr></td><td><a href=\"soft_symlink_example.html\">soft_symlink_example.html</a></td><td class=\"numeric\">19 B</td></tr><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"html&lt;unsafe&amp;weird&gt;filename.html\">html&lt;unsafe&amp;weird&gt;filename.html</a></td><td class=\"numeric\">88 B</td></tr><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"some_file_name.html\">some_file_name.html</a></td><td class=\"numeric\">102 B</td></tr></tbody></table><footer><p>Local directory listing generated by Servo.</p></footer></body></html>"
 right: "<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>Directory listing: /home/robert/SoftwareDevelopment/ThirdPartyProjects/servo/tests/wpt/directory-listing-example</title>\n    <style>\n        h1 {\n            text-align: center;\n        }\n        table {\n            font-family: monospace;\n            margin: 1em auto;\n            border-spacing: 0.2em 0.6em;\n            border-collapse: separate;\n            border: 1px solid black;\n            text-align: center;\n        }\n        th[scope=col] {\n            background-color: #ddd;\n            border: 1px solid #bbb;\n            padding: 0.2em;\n        }\n        th, td {\n            border: 1px solid #ddd;\n            padding: 0.2em;\n        }\n        td.numeric {\n            text-align: right;\n        }\n        footer {\n            margin-top: 1em;\n            border-top: 1px solid gray;\n            font-size: small;\n            color: gray;\n            text-align: right;\n        }\n    </style>\n</head>\n<body><h1>Index of /home/robert/SoftwareDevelopment/ThirdPartyProjects/servo/tests/wpt/directory-listing-example</h1><table><thead><tr><th scope=\"col\">Type</th><th scope=\"col\">Name</th>\n<th scope=\"col\">Size</th></tr></thead><tbody><tr><td><abbr title=\"directory\">D</abbr></td><td><a href=\"sub-directory-example\">sub-directory-example/</a></td><td class=\"numeric\">-</td></tr><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"some_file_name.html\">some_file_name.html</a></td><td class=\"numeric\">102 B</td></tr><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"html&lt;unsafe&amp;weird&gt;filename.html\">html&lt;unsafe&amp;weird&gt;filename.html</a></td><td class=\"numeric\">88 B</td></tr><tr><td><abbr title=\"symlink\">S</abbr></td><td><a href=\"soft_symlink_example.html\">soft_symlink_example.html</a></td><td class=\"numeric\">19 B</td></tr><tr><td><abbr title=\"directory\">D</abbr></td><td><a href=\"html&lt;unsafe&amp;weird&gt;directoryname\">html&lt;unsafe&amp;weird&gt;directoryname/</a></td><td class=\"numeric\">-</td></tr><tr><td><abbr title=\"file\">F</abbr></td><td><a href=\"README.txt\">README.txt</a></td><td class=\"numeric\">375 B</td></tr></tbody></table><footer><p>Local directory listing generated by Servo.</p></footer></body></html>"
stack backtrace:

@Bobulous
Copy link
Author

Yeah, I've realised a few problems with the unit test (and you've just spotted a new one):

  • Git ignores empty directories, and I didn't put anything into the example sub-directories, so Git did not add them to the commit, and so the generated HTML will not match the expected HTML.
  • The unit test relies on the directory contents being iterated in the same order, and I'm guessing this will vary from one platform to another, so again the generated and expected HTML will not necessarily match.
  • And you've just pointed out that the local path will not be /home/robert when run by anyone other than me (or other people called Robert) so again a mismatch between generated and expected.

I've added tiny non-empty files to the example sub-directories within tests/wpt/directory-listing-example so that Git does include these sub-directories in the next commit. And I'm going to rework the HTML generation so that it lists directories first and then files, each in alphabetical order, which should mean that the order is the same for everyone (though I'm not sure what Windows will do with the soft symlink).

Not sure yet how to get the correct path to appear in the expected HTML for the unit test, but I'll come up with something.

@Bobulous
Copy link
Author

I think the new unit tests should now pass successfully, but please do not approve/merge this yet because there is still a technical problem which I need advice in order to fix.

Changes:

The directory listing now groups sub-directories and files separately, and lists each in alphabetical order (well, whatever order OsString considers natural). And the unit test now reads the directory path instead of expecting the hardcoded path specific to my machine. And I added in the missing sub-directories. So the directory listing unit test should now pass.

Problems:

I've realised that the directory listing renders fine now, but clicking on the links to files/directories does not work unless the URL (in the address bar) ends with a forward-slash. If the forward-slash is missing then Servo does not realise it's in a directory and clicking on any of the links causes Servo to replace the current directory name with whatever was clicked. Which obviously means the page will fail to load.

Example:
Page URL is /home/bob/tests/wpt/directory-listing-example (with no forward-slash at the end).
User clicks on README.txt link on the listing page.
Servo (thinking it's in the wpt directory) tries to load the path /home/bob/tests/wpt/README.txt which is not as intended.

Example:
Page URL is /home/bob/tests/wpt/directory-listing-example/ (with forward-slash at the end).
User clicks on README.txt link on the listing page.
Servo (understanding it's in the directory-listing-example directory) tries to load the path /home/bob/tests/wpt/directory-listing-example/README.txt and the file does show up as intended.

So I need to work out how to tell Servo that it's inside the directory even if the URL in the address bar does not end with a forward-slash.

@Bobulous
Copy link
Author

I think the third commit has fixed the problem related to local directory paths not ending with a forward-slash in the address bar. The links in the directory listing now work whether or not the path in the address bar ends with a forward-slash, though I'm wondering whether there's a way to force the address bar to update so that there's no difference at all between visiting a local directory path with or without the forward-slash.

Copy link
Member

@mrobinson mrobinson 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 kicking this off. This should be simplified before it can be merged.

  1. Directories and files should be listed together in alphabetical order. Make sure that ".." is included.
  2. Instead of using tables, push a structure of <div>, which are styled using classes. This will allow more flexible styling simply by modifying the CSS. This structure should be as simple as possible and be valid modern non-quirks HTML5. For example:
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="">
</head>
<body>
<div class="directory-listing">
  <div class="file">
    <div class="name">filename.txt</div>
    <div class="size">10 KB</div>
    <div class="last-modified">01/01/2024</div>
  </div>
</div>
</body>
</html>

components/net/fetch/methods.rs Outdated Show resolved Hide resolved
components/net/fetch/methods.rs Outdated Show resolved Hide resolved
components/net/fetch/methods.rs Outdated Show resolved Hide resolved
components/net/fetch/methods.rs Outdated Show resolved Hide resolved
@Bobulous
Copy link
Author

Thank you the review, @mrobinson.

I'm going to try to get the CSS into an external file now.

But I'm going to leave the markup in the form of a table so that it remains accessible to users who rely on screen readers, and other tools which rely on semantic markup. With the additional class attributes you've suggested, we will be able to use CSS to break it out of a table layout if we prefer that look for users who don't rely on accessibility tools. (For example, we can define CSS such as table {display: block} to remove the table nature of the table element.)

If you're certain it should not be a table structure, and you're sure it won't hurt accessibility to move it out of a table, then let me know. But I reckon we can have the best of both by using class attributes and CSS styling.

@mrobinson
Copy link
Member

FWIW Servo has no accessibility support at the moment so this conversation is highly hypothetical.

That said, I'm not sure that a table is really helpful for AT users here. If table display is necessary it can be applied via CSS with display: table and friends. Let's keep the HTML as simple as possible.

@Bobulous
Copy link
Author

Okay, I have CSS working as an external resources file now. And directories and files are now together, rather than in two groups. And I've added a link to the parent directory (unless the path has no parent).

My thinking with regards to tables versus divs is based on advice such as found here. But if Servo is not yet in a place where screen readers can play with it, then I'll change table, tr, td, etc, into div with class attributes.

One thing I still haven't found an answer for: how do we format a SystemTime into a readable datetime string (with the sort of format you've shown in your example HTML)? I found a way to do this in the newest versions of time-rs, but I can't find a way to do this using the version of time-rs which is currently in use in the Servo project.

@Bobulous
Copy link
Author

Hello, @mrobinson.

I've moved the CSS into a file in the resources directory. Replaced table with nested div elements. Added a link up to the parent directory. And I've improved the presentation of file sizes which are too big to be shown in bytes.

Still need to work out how to format SystemTime into something suitable for display. And need to look again at the way I'm adding a forward-slash to the end of the URL.

Can you review the latest revision and let me know how it's all looking at the moment?

@fabricedesre
Copy link
Contributor

One thing I still haven't found an answer for: how do we format a SystemTime into a readable datetime string (with the sort of format you've shown in your example HTML)? I found a way to do this in the newest versions of time-rs, but I can't find a way to do this using the version of time-rs which is currently in use in the Servo project.

Servo depends both on time@0.1.45 and time@0.3.36 because the cookie crate pulls the 0.3 version in. Is that the new one you want to use?

@Bobulous
Copy link
Author

Bobulous commented May 1, 2024

@fabricedesre, yes the 0.3.36 version of the time crate will work nicely. (It provides an OffsetDateTime which has methods such as year() and month(), and it implements From<SystemTime>.)

How can I make use of the 0.3.36 version within just the one module, without breaking things anywhere else? Oh, and I've just spotted that From<SystemTime> says "Available on crate feature std only" . . . do you know whether cookie is pulling in the time crate with the "std" feature enabled?

@Bobulous
Copy link
Author

Bobulous commented May 2, 2024

@mrobinson @fabricedesre The only way I can find to make the 0.3 version of the time crate available is to add something like this to the Cargo.toml file:

[dependencies]
// Leave existing line as it is.
time = "0.1.41"
// And add alias for the newer time package.
time_03 = { package = "time", version = "0.3" }

That way, I can refer to the time_03 crate in the methods which need to format SystemTime into a human-friendly datetime, but everything else in the Servo code will continue to use the 0.1 version which it currently relies on.

Does this approach seem acceptable, or do you know of a better way?

@fabricedesre
Copy link
Contributor

@Bobulous yes using a package alias is how this kind of problem is solved.

@Bobulous
Copy link
Author

Bobulous commented May 3, 2024

Thank you, @fabricedesre, I've got the last-modification date printing nicely now.

I'm now working on the stylesheet, and on adjusting the directory listing unit test (because last-modification date is going to be a pain to match exactly, assuming the timestamps get adjusted every time someone checks out the project).

@Bobulous
Copy link
Author

Bobulous commented May 4, 2024

@mrobinson I've made numerous changes, including the addition of actual last-modified dates, and more readable file sizes.

I suspect you might say you hate the CSS styling I've applied (I was looking at the Servo logo and website when choosing the colours), and may ask for the HTML to be simplified further, but please take a look and let me know what you'd like to see changed.

@sagudev
Copy link
Member

sagudev commented May 5, 2024

Maybe instead of querying real fs in unit test, virtual fs can be constructed instead at the start of test.

Also having test files in tests/wpt seems weird (as these tests are not intended to be used with wpt testing framework, right?).

@Bobulous
Copy link
Author

Bobulous commented May 5, 2024

Ah, I'm presuming the benefit of that would be:

  1. No need to waste space with an actual test folder and sub-folders all holding example files.
  2. The virtual file system could describe files of huge sizes, allowing the unit test to check the presentation of large file sizes, whereas an actual test folder cannot afford to waste so much space.
  3. The properties of the virtual filesystem directory could be controlled exactly, so the unit test would only need to worry about presentation (HTML output) and not about specifics (last-mod dates) allowing the unit test to go back to being a simple raw HTML comparison without all of the value injection and redaction.

Are there any disadvantages?

Assuming not, can you point me to an example of defining a virtual file system for a unit test, @sagudev?

@Bobulous
Copy link
Author

Bobulous commented May 5, 2024

@sagudev, I'm not having any luck finding a viable way of creating a virtual file system which Servo would then be able to read from (nothing compatible with ServoUrl). If I'm missing something, let me know.

So now I'm thinking of splitting up the functionality into steps:

  1. Turn a path into a struct which describes the directory (has_parent and items).
  2. Turn that struct into HTML which describes the directory.

That way, the unit test can be split into tests for each step, and then the current all-in-one test can be changed so that it just checks that actually asking Servo to visit an existing local directory will cause an HTML response to be returned which does not contain the something-went-wrong message (but does not check the exact content of the HTML).

This will make it easy to test the HTML generation step for a variety of scenarios, because the directory summary struct can be easily mocked for each test. It won't be possible to reliably check the directory-to-struct step, though, because we'll have the same problem as before: no reliably stable directory. But if the test points to the servo project root directory, then at least the unit test can assert that a "Cargo.toml" and "Cargo.lock" file must exist, and that sub-directories must exist too.

However, if I'm missing an easy way to simply define a virtual directory which can be fed to ServoUrl for the unit test, that would be a better way to proceed.

@sagudev
Copy link
Member

sagudev commented May 6, 2024

Yeah, I looked again and it seems that all require wrapper for fs functions. But there are crates for tmp directories, so those can be used and manually populated at the start of the test.

https://docs.rs/tempfile/latest/tempfile/fn.tempdir.html is already in servo's dependency graph.

@Bobulous
Copy link
Author

Bobulous commented May 6, 2024

Okay, I'll use tempfile to create a temporary directory instead of targeting an actual project directory.

But it seems that tempfile does not offer a way to adjust the last-modified dates of the temporary files that it creates, so it will still lead to the problem of not being able to check that last-modified datetimes are rendered correctly in HTML.

Consequently, I'm trying to write unit tests which check the individual steps (such as render correct HTML from an artificially created directory summary) but I'm having trouble getting the functions to be visible to the unit test file.

I was under the impression that pub(crate) should work, because the components/net/tests/fetch.rs file is in the same crate as components/net/fetch/methods.rs file, right? (The Cargo.toml file is in components/net and there are no more Cargo.toml files below that, so doesn't that define components/net as the crate root?) But I must have something wrong because pub(crate) is causing compilation errors, forcing me to use pub at the moment. These methods should not be fully public, though, so I need advice about this.

@Bobulous
Copy link
Author

Bobulous commented May 6, 2024

I've removed the actual demo directory, and tempfile is now used to create a temporary directory for the unit tests which need to read a known list of file and subdirectory items. (Still can't set the last-mod dates, though.)

The unit tests have been reworked, but I still want to reduce the visibility of the new functions/structs so that they can only be seen from within the methods.rs file and from within the unit tests.

I'm hoping someone can review the latest version and tell me what works, what needs adjustment, and what needs to be scrapped. I've spent seven hours on this today, so I think I'd better wait for some feedback before doing any more.

Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
…ries. Updated unit tests.

Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
…ble with nested div elements.

Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
…Added last-modification datetime for files.

Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
…tual demo directory.

Signed-off-by: Bobulous <Bobulous@users.noreply.github.com>
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.

Add local directory listing feature
4 participants