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

Traverse #1943

Merged
merged 8 commits into from
Mar 8, 2019
Merged

Traverse #1943

merged 8 commits into from
Mar 8, 2019

Conversation

sarvaje
Copy link
Contributor

@sarvaje sarvaje commented Feb 20, 2019

Pull request checklist

Make sure you:

For non-trivial changes, please make sure you also:

  • Added/Updated related documentation.
  • Added/Updated related tests.

Short description of the change(s)

The bundle size using this solution increase less than 500kb.

I still have to test it and see that everything is working as expected.

Also I need to polish the code, right now I just wanted to have something working to see the bundle size.

@molant @antross any feedback would be great.

Copy link
Member

@molant molant left a comment

Choose a reason for hiding this comment

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

We should also look into removing code from location-helpers.ts when possible. There's a lot of black magic going on in there that shouldn't be needed anymore (hopefully).

packages/connector-jsdom/src/connector.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/utils/dom/new-traverse.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/types/new-async-html.ts Outdated Show resolved Hide resolved
@sarvaje
Copy link
Contributor Author

sarvaje commented Feb 25, 2019

@antross @molant I think this is ready to review

@molant
Copy link
Member

molant commented Feb 26, 2019 via email

@sarvaje
Copy link
Contributor Author

sarvaje commented Feb 26, 2019

AFAIK, no, the parser will have the evalute.

@molant
Copy link
Member

molant commented Feb 26, 2019

Aghhh email answers don't put the comment in the right converation.

I thought we agreed that connectors should do the evaluate and parser-html the traversing and element location. Browsers have better API support, the jsdom connecter already generates a context for this so we will be duplicating and the goal of this PR was to not have jsdom in the browser extension neither 😕

Maybe we should all sync about this?

@sarvaje
Copy link
Contributor Author

sarvaje commented Feb 26, 2019

@molant maybe there is a little confusion here.

The connectors will use their own evalute, but parse-html will allow to run scripts using jsdom, so the connector-local will be able to run scripts.

The traverse was remove from the parser-html.

Now the traverse is just an util inside hint.

@sarvaje
Copy link
Contributor Author

sarvaje commented Feb 27, 2019

@molant @antross test passing!! (at least on Linux hehehe)

packages/connector-jsdom/src/connector.ts Outdated Show resolved Hide resolved
packages/connector-jsdom/src/connector.ts Outdated Show resolved Hide resolved
packages/connector-jsdom/src/resource-loader.ts Outdated Show resolved Hide resolved
private setFetchElement(event: FetchEnd) {
const url = event.request.url;
const elements = Array.from(document.querySelectorAll('[href],[src]')).filter((element: any) => {
const elements = Array.from(this._document.querySelectorAll('[href],[src]')).filter((element: any) => {
Copy link
Member

Choose a reason for hiding this comment

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

You also needed to resolve the original element for FetchEnd in other connectors too, right? Should this be a shared helper? If so, we may also need to include poster, data, and srcset for completeness.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you thinking in a new util in hint?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm taking a look to this and they are not compatible (at least not now). In jsdom, we have the element that is fetching the content, but not in the browser extension.

Also, I found another problem here, in case of a relative url, event.request.url will contain the full url (domain + relative url), but the element will contain the relative url, so this code won't work.

I'm thinking in something like url.endsWith(element.getAttribute('src')) || .....

Maybe there is another way, but I don't know this code too much.

What do you think @antross?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If so, we may also need to include poster, data, and srcset for completeness.

@antross what elements use the attribute 'data'

Copy link
Member

Choose a reason for hiding this comment

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

From https-only:

/*
 * Possible URL sources:
 *
 * * <img>, <audio>, <video>, <source> and <track> can have a `src` attribute.
 *   E.g.: <img src="http://example.com/image.jpg">
 *
 * * <img> and <source> can have a `srcset` attribute.
 *   https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source
 *   E.g.: <img src="image-src.png" srcset="image-1x.png 1x, image-2x.png 2x">
 *
 * * <video>  can have a `poster` attribute.
 *   E.g.: <video width="480" controls poster="https://archive.org/download/WebmVp8Vorbis/webmvp8.gif" />
 *
 * * <object> has a `data` attribute.
 *   E.g.: <object data="movie.swf" type="application/x-shockwave-flash"></object>
 *   https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object
 */

There's also srcset that uses another format that we should probably take into consideration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm taking into consideration this format image-1x.png 1x, image-2x.png 2x. There is any other format?

packages/hint/src/lib/types/html.ts Show resolved Hide resolved
packages/hint/src/lib/types/html.ts Outdated Show resolved Hide resolved
packages/parser-html/README.md Outdated Show resolved Hide resolved
@sarvaje sarvaje force-pushed the traverse branch 2 times, most recently from f8f9340 to d5f8193 Compare March 1, 2019 20:44
Copy link
Member

@antross antross left a comment

Choose a reason for hiding this comment

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

A few nits and one big item: let's fully purge the IAsync* classes and interfaces in this PR since we've already updated most code not to use them.

packages/hint/src/lib/utils/dom/traverse.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/utils/dom/traverse.ts Outdated Show resolved Hide resolved
[key: string]: string;
};

export type ParsedHTMLElement = Element & {
Copy link
Member

Choose a reason for hiding this comment

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

Does a ParsedHTMLElement actually include support for everything Element defines (since this seems to refer to the built-in Element type)?

I suspect not since we're using an attribs collection instead of attributes. I think it's better to explicitly define all the properties we expect here if we don't have a suitable type from parse5 to use instead of deriving from Element. I'm concerned we could accidentally reference things that don't exist here in the future otherwise.

Copy link
Member

Choose a reason for hiding this comment

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

Also, this type should be able to be private to this file.

Suggested change
export type ParsedHTMLElement = Element & {
type ParsedHTMLElement = Element & {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does a ParsedHTMLElement actually include support for everything Element defines (since this seems to refer to the built-in Element type)?

Nop. It support just some element properties.

return Object.entries(x).map((y) => {
return {
name: y[0],
value: y[1] as string
Copy link
Member

Choose a reason for hiding this comment

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

Why as string, is null possible here? If so, we should probably update our type instead of casting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like that casting is outdated. We don't need it anymore.

packages/hint/src/lib/types/async-html.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/types/html.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/types/jsdom-async-html.ts Outdated Show resolved Hide resolved
packages/hint/src/lib/utils/dom/create-async-window.ts Outdated Show resolved Hide resolved
@sarvaje
Copy link
Contributor Author

sarvaje commented Mar 4, 2019

@antross done!

Copy link
Member

@antross antross left a comment

Choose a reason for hiding this comment

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

Looks like we're not quite getting correct position information in extension-vscode (underlines appear on the line following the actual problem). Try installing the extension, then using it on the hint repo, editing packages/extension-browser/src/devtools/panel/panel.html to look something like:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf8">
        <title>Hints</title>
    </head>
    <body>
        <script src="panel.js"></script>
        <img src="test.png">
        <button>Test</button>
    </body>
</html>

This will trigger hints for meta-charset-utf8 and button-type (among others). I also didn't see any reports coming from axe in this context which may suggest our eval code isn't working as expected with connector-local.

@antross
Copy link
Member

antross commented Mar 5, 2019

Also, I took this for a spin with extension-browser which still worked as advertised. Test bundle here if anyone else wants to try:

@sarvaje
Copy link
Contributor Author

sarvaje commented Mar 5, 2019

@antross does the extension needs a zero based location?

If I can reproduce your error, but If I use the local connector for the same file, I get this result:
image

As you can see, the locations are good.

P.S.: In this capture, the issue with axe is fixed hehehe.

@sarvaje sarvaje force-pushed the traverse branch 3 times, most recently from 523a11b to 0c14004 Compare March 6, 2019 17:11
@sarvaje sarvaje force-pushed the traverse branch 2 times, most recently from e79317f to 4f71371 Compare March 6, 2019 18:34
Copy link
Member

@antross antross left a comment

Choose a reason for hiding this comment

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

Looks great (once we finish updating the tests), thanks @sarvaje for being patient with me on this one! This is a really important refactor to unblock a number of other improvements. 🎉

@sarvaje
Copy link
Contributor Author

sarvaje commented Mar 6, 2019

Yeah!

thanks @sarvaje for being patient with me on this one!

hehehe, no problem!!

Copy link
Member

@molant molant left a comment

Choose a reason for hiding this comment

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

Awesome work. Just a few more changes and we can get this merge! We might want to do a release before merging everything though.
Another thing that worries me is the commit history, how do we want to tackle it? hint is a Breaking but what about the other ones? The output and public API doesn't change so they should probably be patch or minor in most cases?


export default (dom: HTMLDocument, url: string): HTMLElement | null => {
const elements = dom.querySelectorAll('[href],[src],[poster],[srcset]').filter((element: any) => {
const elementUrl = element.getAttribute('href') || element.getAttribute('src') || element.getAttribute('poster');
Copy link
Member

Choose a reason for hiding this comment

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

If the dom doesn't change (and IIRC it shouldn't) we should probably cache a few things like the list of all the elements with a URL and absoluteUrls. Probably something that doesn't have to be done in this PR but we should open an issue. Thoughts @webhintio/core ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this is going to be called more than once with the same url (if so, is because the user has something duplicated is the code). But we can cache the result of the querySelectorAll.

Copy link
Member

Choose a reason for hiding this comment

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

Probably a premature optimization at this point. I'd wait.

Copy link
Member

Choose a reason for hiding this comment

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

We might end up creating the same new Url quite a few times depending on how many links the page has.
Can I have a //TODO here and hopefully one day we will do #210 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We might end up creating the same new Url quite a few times depending on how many links the page has.

We shouldn't have the same url repeated many often, the only scenario I can think about is when we have the same image in few places.

I will add the //TODO

return false;
});

if (elements.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Add a comment here to explain saying something like:

Even if there are multiple elements with the same URL, it's the first one that triggers the download in the browser and thus the one we should be reporting.

const attribute = element.attributes.item(i);

if (attribute) {
query += `[${attribute.name}="${attribute.value}"]`;
Copy link
Member

Choose a reason for hiding this comment

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

Don't we have to do any escaping here? We are doing something similar in location-helpers.ts right now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can use get-element-by-url.ts to do this.

Copy link
Member

Choose a reason for hiding this comment

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

SGTM

packages/connector-jsdom/src/resource-loader.ts Outdated Show resolved Hide resolved
@@ -169,10 +149,10 @@ export default class WebExtensionConnector implements IConnector {
}

public evaluate(source: string): Promise<any> {
return Promise.resolve(this._window.evaluate(source));
return Promise.resolve(eval(source)); // eslint-disable-line no-eval
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 add a comment explaining that this script is injected into the page and that's why we are using eval (because that's what's happening, right?)

packages/hint/src/lib/cli/analyze.ts Outdated Show resolved Hide resolved
packages/hint/docs/contributor-guide/how-to/connector.md Outdated Show resolved Hide resolved
packages/hint/docs/contributor-guide/how-to/hint.md Outdated Show resolved Hide resolved
packages/hint/package.json Outdated Show resolved Hide resolved
@sarvaje
Copy link
Contributor Author

sarvaje commented Mar 7, 2019

@molant I think everything should be done, except the commits stuff

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.

3 participants