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

SSR: Stringify VNodes (II) #1344

Closed
Show file tree
Hide file tree
Changes from 85 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
145e331
wip
philip-peterson Jun 21, 2020
3c01c22
rustfmt
philip-peterson Jun 21, 2020
169a3e2
VList, VText
philip-peterson Jun 21, 2020
9eb65ce
finish
philip-peterson Jun 21, 2020
47789d9
format
philip-peterson Jun 21, 2020
bf59552
fix bug
philip-peterson Jun 22, 2020
e17cf3c
revise vtag test
philip-peterson Jun 22, 2020
e32c9d6
vtext test
philip-peterson Jun 22, 2020
d46372e
cleanup warnings
philip-peterson Jun 22, 2020
c7d2615
restore unimplemented!()
philip-peterson Jun 22, 2020
051917c
Add test for refs stringifying
philip-peterson Jun 22, 2020
de7a3b0
cargo fmt
philip-peterson Jun 22, 2020
08bfaa5
add comment
philip-peterson Jun 23, 2020
decfd19
PR feedback
philip-peterson Jun 23, 2020
abb01d7
undo whitespace changes
philip-peterson Jun 23, 2020
772211e
TryFrom
philip-peterson Jun 23, 2020
3291ec1
cargo fmt
philip-peterson Jun 23, 2020
0f3b7f0
fix import
philip-peterson Jun 23, 2020
961a507
fix tests
philip-peterson Jun 23, 2020
7373898
cargo fmt
philip-peterson Jun 23, 2020
98a2b83
return errors instead of panicking
philip-peterson Jun 23, 2020
19c7f58
htmlstringifyerror -> htmlrendererror
philip-peterson Jun 26, 2020
cea6a7b
PR feedback
philip-peterson Jun 26, 2020
aad03df
push_str
philip-peterson Jun 26, 2020
2da0666
cargo fmt
philip-peterson Jun 26, 2020
9deee41
add tag validation
philip-peterson Jun 27, 2020
e289b08
fmt
philip-peterson Jun 27, 2020
2e65887
formatting
philip-peterson Jun 27, 2020
f5f7872
move tag data to other crate
philip-peterson Jun 27, 2020
bd555c9
move out of yewtil
philip-peterson Jun 27, 2020
bd2c5ae
SSR -> SMR
philip-peterson Jun 27, 2020
931550b
format
philip-peterson Jun 27, 2020
bd4935a
refactor
philip-peterson Jun 27, 2020
1ba0c88
move lambda
philip-peterson Jun 27, 2020
ee9d8c9
fix
philip-peterson Jun 27, 2020
e9ce780
use once_cell
philip-peterson Jun 27, 2020
cbff3a6
cargo fmt
philip-peterson Jun 27, 2020
6fbce93
use standard library for alphanum
philip-peterson Jun 27, 2020
ee0e1f4
util for is_valid_html_attribute_name
philip-peterson Jun 27, 2020
cd171d6
use attribute validator
philip-peterson Jun 27, 2020
c4175e1
fix formatting
philip-peterson Jun 27, 2020
1f7c915
Add missing dependencies to `yew_stdweb`.
teymour-aldridge Jun 27, 2020
f159803
iff -> when
philip-peterson Jun 27, 2020
ce8523c
add some tests
philip-peterson Jun 27, 2020
5f78a32
use lazy_static
philip-peterson Jun 27, 2020
459aa6d
Merge pull request #2 from teymour-aldridge/pull/1344
philip-peterson Jun 27, 2020
998403b
Merge branch 'peterson/stringify_node_2' of github.com:philip-peterso…
philip-peterson Jun 27, 2020
fb275b4
:/ just realized yew-stdweb doesnt need these, rolling back
philip-peterson Jun 27, 2020
53e23c8
fix tests
philip-peterson Jun 27, 2020
5734cf7
tests were not testing the things their names indicated
philip-peterson Jun 27, 2020
3ccf96e
nit: test naming
philip-peterson Jun 27, 2020
68fac3d
Update yew/src/virtual_dom/mod.rs
philip-peterson Jun 27, 2020
959f36b
Use unwrap_or_else
philip-peterson Jun 28, 2020
98c867a
Use {0}
philip-peterson Jun 28, 2020
324a23a
no checked=true
philip-peterson Jun 28, 2020
bd0091a
Merge branch 'peterson/stringify_node_2' of github.com:philip-peterso…
philip-peterson Jun 28, 2020
1b2602b
use {0}
philip-peterson Jun 28, 2020
a91b402
use & instead of as_ref
philip-peterson Jun 28, 2020
c53419d
fix error
philip-peterson Jun 28, 2020
12e28e6
html.string -> html.html
philip-peterson Jun 28, 2020
e2f1e5d
move all SMR impls to smr.rs
philip-peterson Jun 28, 2020
dc0b2df
Move all vdom smr impls into smr
philip-peterson Jun 28, 2020
32c6032
move vtext tests
philip-peterson Jun 28, 2020
131b60a
move tests to smr.rs
philip-peterson Jun 28, 2020
52f7511
Documentation tweaks, fix build error
philip-peterson Jun 28, 2020
329c14c
fix test
philip-peterson Jun 28, 2020
99057bf
cargo fmt
philip-peterson Jun 28, 2020
f5bc8b4
add SMR to list of doc test features
philip-peterson Jun 28, 2020
858f621
fix link refs
philip-peterson Jun 28, 2020
7804cff
Fix warning
philip-peterson Jun 28, 2020
34e286a
add SMR to yew-stdweb docs
philip-peterson Jun 28, 2020
d5c3c7b
remove as_ref
philip-peterson Jun 28, 2020
acac47d
Rename `Html` to `HtmlString`.
teymour-aldridge Jun 28, 2020
e5fe5c1
Merge pull request #3 from teymour-aldridge/pull/1344
philip-peterson Jun 28, 2020
5c2a60f
Update yew/src/virtual_dom/smr.rs
philip-peterson Jun 28, 2020
12dd296
Implement Clone for HtmlString
philip-peterson Jun 28, 2020
2d3d032
Better names for tests
philip-peterson Jun 28, 2020
93e90f6
fix tests, warnings, tests not running (D:)
philip-peterson Jun 29, 2020
d21a7db
Remove re-export
philip-peterson Jun 29, 2020
a783503
cargo fmt
philip-peterson Jun 29, 2020
215119c
Merge branch 'peterson/stringify_node_2' of github.com:philip-peterso…
philip-peterson Jun 29, 2020
dbcfe49
Remove lists of tags
philip-peterson Jun 29, 2020
ac5e6f2
fix test
philip-peterson Jun 29, 2020
3de8c2f
add more tests
philip-peterson Jun 29, 2020
7d9e907
allow svg style elements
philip-peterson Jun 29, 2020
b79f55b
formatting
philip-peterson Jul 1, 2020
c7986c9
remove code deemed dead
philip-peterson Jul 1, 2020
b5d87b3
PR feedback
philip-peterson Jul 4, 2020
3358255
add structure for yewtil-validation crate
philip-peterson Jul 4, 2020
e31ac77
update comment
philip-peterson Jul 4, 2020
04dbbad
fix up references to yew_validation
philip-peterson Jul 4, 2020
ffca187
remove yew-validation from this PR
philip-peterson Jul 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ci/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ set -euxo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_

(cd yew \
&& cargo test --target wasm32-unknown-unknown --features wasm_test \
&& cargo test --target wasm32-unknown-unknown --features wasm_test,sans_mount_render \
&& cargo test --doc --features doc_test,wasm_test,yaml,msgpack,cbor,toml \
&& cargo test --doc --features doc_test,wasm_test,yaml,msgpack,cbor,toml \
--features std_web,agent,services --no-default-features)
--features std_web,agent,services,sans_mount_render --no-default-features)

(cd yew-functional && cargo test --target wasm32-unknown-unknown)

Expand Down
3 changes: 2 additions & 1 deletion yew-stdweb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ gloo = { version = "0.2.1", optional = true }
http = "0.2"
indexmap = "1.0.2"
js-sys = { version = "0.3", optional = true }
lazy_static = "1.3.0"
log = "0.4"
proc-macro-hack = "0.5"
proc-macro-nested = "0.1"
Expand Down Expand Up @@ -78,7 +79,7 @@ msgpack = ["rmp-serde"]
cbor = ["serde_cbor"]

[package.metadata.docs.rs]
features = ["yaml", "cbor", "toml", "msgpack", "doc_test"]
features = ["yaml", "cbor", "toml", "msgpack", "doc_test", "sans_mount_render"]

[workspace]
members = [
Expand Down
5 changes: 4 additions & 1 deletion yew/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ console_error_panic_hook = { version = "0.1", optional = true }
futures = { version = "0.3", optional = true }
gloo = { version = "0.2.1", optional = true }
http = "0.2"
htmlescape = { version = "0.3.1", optional = true }
indexmap = "1.0.2"
js-sys = { version = "0.3", optional = true }
log = "0.4"
lazy_static = "1.3.0"
proc-macro-hack = "0.5"
proc-macro-nested = "0.1"
rmp-serde = { version = "0.14.0", optional = true }
Expand Down Expand Up @@ -133,10 +135,11 @@ web_sys = ["console_error_panic_hook", "futures", "gloo", "js-sys", "web-sys", "
doc_test = []
wasm_test = []
services = []
sans_mount_render = ["htmlescape"]
agent = ["bincode"]
yaml = ["serde_yaml"]
msgpack = ["rmp-serde"]
cbor = ["serde_cbor"]

[package.metadata.docs.rs]
features = ["yaml", "cbor", "toml", "msgpack", "doc_test"]
features = ["yaml", "cbor", "toml", "msgpack", "doc_test", "sans_mount_render"]
2 changes: 2 additions & 0 deletions yew/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ mod scheduler;
pub mod utils;
pub mod virtual_dom;

pub mod sgml_tags;

#[cfg(feature = "agent")]
pub mod agent;
#[cfg(feature = "services")]
Expand Down
230 changes: 230 additions & 0 deletions yew/src/sgml_tags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
//! This module contains utilities for parsing or validating strings relating
//! to tags.

use lazy_static::lazy_static;
use std::collections::HashSet;
use std::iter::FromIterator;

lazy_static! {
static ref DISALLOWED_CUSTOM_ELEMENT_TAGS: HashSet<&'static str> = HashSet::from_iter(
vec![
"annotation-xml",
"color-profile",
"font-face",
"font-face-src",
"font-face-uri",
"font-face-format",
"font-face-name",
"missing-glyph",
]
.iter()
.map(|tag| tag.clone())
);
Copy link
Member

Choose a reason for hiding this comment

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

No need to iter and clone here.

Suggested change
static ref DISALLOWED_CUSTOM_ELEMENT_TAGS: HashSet<&'static str> = HashSet::from_iter(
vec![
"annotation-xml",
"color-profile",
"font-face",
"font-face-src",
"font-face-uri",
"font-face-format",
"font-face-name",
"missing-glyph",
]
.iter()
.map(|tag| tag.clone())
);
static ref DISALLOWED_CUSTOM_ELEMENT_TAGS: HashSet<&'static str> = HashSet::from_iter(vec![
"annotation-xml",
"color-profile",
"font-face",
"font-face-src",
"font-face-uri",
"font-face-format",
"font-face-name",
"missing-glyph",
]);

Copy link
Member

Choose a reason for hiding this comment

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

Please note that if you agree with my comment further down then this list will no longer be necessary.
At that point we can remove the lazy_static dependency.

}

/// Returns true when the character provided is a "control" as defined
/// in [the WhatWG spec](https://infra.spec.whatwg.org/#control)
fn is_control(c: char) -> bool {
match c {
'\u{007F}'..='\u{009F}' => true,
_ => is_c0_control(c),
}
}

/// Returns true when the character provided is a "c0 control" as defined
/// in [the WhatWG spec](https://infra.spec.whatwg.org/#c0-control)
fn is_c0_control(c: char) -> bool {
match c {
'\u{0000}'..='\u{001F}' => true,
_ => false,
}
}

/// Returns true when the string provided is a "noncharacter" as defined
/// in [the WhatWG spec](https://infra.spec.whatwg.org/#noncharacter)
fn is_noncharacter(c: char) -> bool {
match c {
'\u{FDD0}'..='\u{FDEF}' => true,
'\u{FFFE}' | '\u{FFFF}' | '\u{1FFFE}' | '\u{1FFFF}' | '\u{2FFFE}' | '\u{2FFFF}'
| '\u{3FFFE}' | '\u{3FFFF}' | '\u{4FFFE}' | '\u{4FFFF}' | '\u{5FFFE}' | '\u{5FFFF}'
| '\u{6FFFE}' | '\u{6FFFF}' | '\u{7FFFE}' | '\u{7FFFF}' | '\u{8FFFE}' | '\u{8FFFF}'
| '\u{9FFFE}' | '\u{9FFFF}' | '\u{AFFFE}' | '\u{AFFFF}' | '\u{BFFFE}' | '\u{BFFFF}'
| '\u{CFFFE}' | '\u{CFFFF}' | '\u{DFFFE}' | '\u{DFFFF}' | '\u{EFFFE}' | '\u{EFFFF}'
| '\u{FFFFE}' | '\u{FFFFF}' | '\u{10FFFE}' | '\u{10FFFF}' => true,
_ => false,
}
}

/// Returns true when the string provided is a valid "attribute name" as defined
/// in [the WhatWG spec](https://html.spec.whatwg.org/multipage/syntax.html#syntax-attribute-name)
pub fn is_valid_html_attribute_name(attr: &str) -> bool {
for c in attr.chars() {
if is_noncharacter(c)
|| is_control(c)
|| c == '\u{0020}'
|| c == '\u{0022}'
|| c == '\u{0027}'
|| c == '\u{003E}'
|| c == '\u{002F}'
|| c == '\u{003D}'
{
return false;
}
}
true
}

/// Returns true when the character provided is a valid PCENChar as defined
/// in [the WhatWG spec](https://html.spec.whatwg.org/multipage/custom-elements.html#prod-pcenchar)
fn is_pcen_char(c: char) -> bool {
match c {
'-' | '.' | '0'..='9' | 'a'..='z' | '_' => true,
'\u{B7}' => true,
'\u{C0}'..='\u{D6}' => true,
'\u{D8}'..='\u{F6}' => true,
'\u{F8}'..='\u{37D}' => true,
'\u{37F}'..='\u{1FFF}' => true,
'\u{200C}'..='\u{200D}' => true,
'\u{203F}'..='\u{2040}' => true,
'\u{2070}'..='\u{218F}' => true,
'\u{2C00}'..='\u{2FEF}' => true,
'\u{3001}'..='\u{D7FF}' => true,
'\u{F900}'..='\u{FDCF}' => true,
'\u{FDF0}'..='\u{FFFD}' => true,
'\u{10000}'..='\u{EFFFF}' => true,
_ => false,
}
}

/// Returns true when the tag name provided would be a valid "custom element" per
/// [the WhatWG spec](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name)
fn is_valid_html_custom_element_name(tag: &str) -> bool {
if DISALLOWED_CUSTOM_ELEMENT_TAGS.contains(&tag) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need this check. Yes, these names must not be used for custom elements but that's only because they are already reserved for non-custom tags.

This function isn't public and is only exposed through is_valid_sgml_tag which already accepts all the tag names in the list in the is_valid_html_standard_ish_element_name check. So currently this check will never return false.

We can add a note to the documentation to make it clear that this function doesn't handle disallowed custom element tags.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the public interface is agnostic to these tags, I see no reason to remove the code. The function is meant to represent the way WhatWG defines a custom tag; to stray outside of that bounds would set a precedent in the codebase for not matching the spec exactly, which is a fraught path to go down when writing code that's meant to be secure.

Copy link
Member

Choose a reason for hiding this comment

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

While I would usually agree, in this case it makes absolutely no sense to incur the performance and space penalty. This is just dead code that Rust can't eliminate.
Currently it's also entirely irrelevant to Yew whether it's a custom element tag or a deprecated tag containing a hyphen.
I totally understand your stance on adhering to the spec - after all, that's the reason we went down this rabbit hole in the first place. But sometimes being pedantic doesn't yield any benefits.
Adding a note to the documentation should also sufficiently take care of your concerns about precedent. Maybe we can even rename the function to contains_valid_html_custom_element_characters to ease your mind?

return false;
}

let mut chars = tag.chars();
let first_char = chars.next();

match first_char {
None => false,
Some(first_char) => {
// must begin with [a-z]
if first_char < 'a' || first_char > 'z' {
return false;
}

let mut seen_hyphen = false;
for c in chars {
if c == '-' {
seen_hyphen = true
}

// all characters must be valid PCENChar's
if !is_pcen_char(c) {
return false;
}
}

// must contain at least one hyphen
seen_hyphen
}
}
}

/// Returns true when the tag name provided looks like a valid non-custom HTML element or valid SVG element.
/// There's no official spec here, it's just arbitrary.
fn is_valid_html_standard_ish_element_name(tag: &str) -> bool {
philip-peterson marked this conversation as resolved.
Show resolved Hide resolved
// must contain at least one character
if tag.is_empty() {
return false;
}

let mut saw_non_hyphen = false;
for c in tag.chars() {
match c {
'a'..='z' |'A'..='Z' | '0'..='9' => {
saw_non_hyphen = true
}
'-' => {}
_ => {
return false;
}
}
}

saw_non_hyphen
}

/// Returns true when you could validly construct a tag using this name in an HTML document
pub fn is_valid_sgml_tag(tag: &str) -> bool {
is_valid_html_standard_ish_element_name(tag) || is_valid_html_custom_element_name(tag)
}

#[cfg(test)]
mod tests {
use super::*;

#[cfg(feature = "wasm_test")]
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};

#[cfg(feature = "wasm_test")]
wasm_bindgen_test_configure!(run_in_browser);

#[test]
fn valid_custom_element() {
assert_eq!(is_valid_html_custom_element_name("foo-bar"), true);
assert_eq!(is_valid_html_custom_element_name("foo-"), true);
assert_eq!(is_valid_html_custom_element_name("bar-baz"), true);
}

#[test]
fn invalid_custom_element() {
assert_eq!(is_valid_html_custom_element_name("foobar"), false);
assert_eq!(is_valid_html_custom_element_name("-bar"), false);
assert_eq!(is_valid_html_custom_element_name("foo bar"), false);
assert_eq!(is_valid_html_custom_element_name(""), false);
assert_eq!(is_valid_html_custom_element_name("foo\nbar"), false);
assert_eq!(is_valid_html_custom_element_name("-"), false);
}

#[test]
fn valid_html_element() {
assert_eq!(is_valid_html_standard_ish_element_name("section"), true);
assert_eq!(is_valid_html_standard_ish_element_name("h2"), true);
assert_eq!(is_valid_html_standard_ish_element_name("applet"), true);
assert_eq!(is_valid_html_standard_ish_element_name("appLET"), true);
assert_eq!(is_valid_html_standard_ish_element_name("aPPlet"), true);
assert_eq!(is_valid_html_standard_ish_element_name("foo-bar"), true);
}

#[test]
fn invalid_html_element() {
assert_eq!(is_valid_html_standard_ish_element_name(" foo"), false);
assert_eq!(is_valid_html_standard_ish_element_name("foo "), false);
assert_eq!(is_valid_html_standard_ish_element_name("-"), false);
assert_eq!(is_valid_html_standard_ish_element_name("!doctype"), false);
}

#[test]
fn valid_html_attribute() {
assert_eq!(is_valid_html_attribute_name("-foo-bar"), true);
assert_eq!(is_valid_html_attribute_name("data-foobar"), true);
assert_eq!(is_valid_html_attribute_name("foo<bar"), true); // shocking but true
}

#[test]
fn invalid_html_attribute() {
assert_eq!(is_valid_html_attribute_name("foo=bar"), false);
assert_eq!(is_valid_html_attribute_name("\"foo\""), false);
assert_eq!(is_valid_html_attribute_name("foo bar"), false);
assert_eq!(is_valid_html_attribute_name("foo>bar"), false);
}

#[test]
fn invalid_sgml_tag() {
assert_eq!(is_valid_sgml_tag("f>bar"), false);
assert_eq!(is_valid_sgml_tag("f<bar"), false);
assert_eq!(is_valid_sgml_tag("/>"), false);
}
}
3 changes: 3 additions & 0 deletions yew/src/virtual_dom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub mod vtag;
#[doc(hidden)]
pub mod vtext;

#[cfg(feature = "sans_mount_render")]
pub mod smr;

use crate::html::{AnyScope, NodeRef};
use cfg_if::cfg_if;
use indexmap::set::IndexSet;
Expand Down
Loading