2021-08-09 05:56:37 -04:00
|
|
|
use crate::common::gen::attrs::ATTRS;
|
|
|
|
use crate::common::gen::codepoints::{WHATWG_ATTR_NAME_CHAR, WHITESPACE};
|
|
|
|
use crate::common::spec::tag::ns::Namespace;
|
2019-12-25 21:47:18 -05:00
|
|
|
use crate::err::ProcessingResult;
|
2020-07-30 05:51:43 -04:00
|
|
|
use crate::proc::checkpoint::WriteCheckpoint;
|
2021-08-08 03:58:10 -04:00
|
|
|
use crate::proc::range::ProcessorRange;
|
2020-01-25 02:04:02 -05:00
|
|
|
use crate::proc::MatchAction::*;
|
|
|
|
use crate::proc::MatchMode::*;
|
2020-01-25 07:05:07 -05:00
|
|
|
use crate::proc::Processor;
|
2021-08-08 03:58:10 -04:00
|
|
|
use crate::unit::attr::value::{
|
|
|
|
process_attr_value, skip_attr_value, DelimiterType, ProcessedAttrValue,
|
|
|
|
};
|
2019-12-25 04:44:51 -05:00
|
|
|
|
|
|
|
mod value;
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
|
|
|
pub enum AttrType {
|
|
|
|
Quoted,
|
|
|
|
Unquoted,
|
|
|
|
NoValue,
|
|
|
|
}
|
|
|
|
|
2019-12-27 05:52:49 -05:00
|
|
|
pub struct ProcessedAttr {
|
|
|
|
pub name: ProcessorRange,
|
|
|
|
pub typ: AttrType,
|
|
|
|
pub value: Option<ProcessorRange>,
|
|
|
|
}
|
|
|
|
|
2021-08-08 03:58:10 -04:00
|
|
|
pub fn process_attr(
|
|
|
|
proc: &mut Processor,
|
|
|
|
ns: Namespace,
|
|
|
|
element: ProcessorRange,
|
|
|
|
) -> ProcessingResult<ProcessedAttr> {
|
2019-12-27 19:21:44 -05:00
|
|
|
// It's possible to expect attribute name but not be called at an attribute, e.g. due to whitespace between name and
|
|
|
|
// value, which causes name to be considered boolean attribute and `=` to be start of new (invalid) attribute name.
|
2021-08-08 03:58:10 -04:00
|
|
|
let name = proc
|
2021-08-08 05:00:51 -04:00
|
|
|
.m(WhileInLookup(WHATWG_ATTR_NAME_CHAR), Keep)
|
2021-08-08 03:58:10 -04:00
|
|
|
.require("attribute name")?;
|
2020-07-29 22:32:53 -04:00
|
|
|
proc.make_lowercase(name);
|
2020-01-23 09:53:09 -05:00
|
|
|
let attr_cfg = ATTRS.get(ns, &proc[element], &proc[name]);
|
2020-01-17 19:42:01 -05:00
|
|
|
let is_boolean = attr_cfg.filter(|attr| attr.boolean).is_some();
|
2020-07-30 05:51:43 -04:00
|
|
|
let after_name = WriteCheckpoint::new(proc);
|
2019-12-25 04:44:51 -05:00
|
|
|
|
2021-08-10 02:12:57 -04:00
|
|
|
// TODO Use attr cfg: collapse, trim, case_sensitive.
|
2021-08-08 03:58:10 -04:00
|
|
|
let should_collapse_and_trim_value_ws =
|
2021-08-10 02:12:57 -04:00
|
|
|
attr_cfg.filter(|attr| attr.collapse && attr.trim).is_some();
|
2020-07-09 03:06:08 -04:00
|
|
|
proc.m(WhileInLookup(WHITESPACE), Discard);
|
2020-01-26 04:32:06 -05:00
|
|
|
let has_value = proc.m(IsChar(b'='), Keep).nonempty();
|
2019-12-25 04:44:51 -05:00
|
|
|
|
2019-12-27 05:52:49 -05:00
|
|
|
let (typ, value) = if !has_value {
|
|
|
|
(AttrType::NoValue, None)
|
2019-12-25 04:44:51 -05:00
|
|
|
} else {
|
2020-07-09 03:06:08 -04:00
|
|
|
proc.m(WhileInLookup(WHITESPACE), Discard);
|
2020-01-07 08:38:42 -05:00
|
|
|
if is_boolean {
|
2020-01-07 19:40:06 -05:00
|
|
|
skip_attr_value(proc)?;
|
2020-01-17 19:42:01 -05:00
|
|
|
// Discard `=`.
|
2020-01-25 07:05:07 -05:00
|
|
|
debug_assert_eq!(after_name.written_count(proc), 1);
|
|
|
|
after_name.erase_written(proc);
|
2020-01-07 08:38:42 -05:00
|
|
|
(AttrType::NoValue, None)
|
|
|
|
} else {
|
2020-01-07 19:40:06 -05:00
|
|
|
match process_attr_value(proc, should_collapse_and_trim_value_ws)? {
|
2020-01-07 08:38:42 -05:00
|
|
|
ProcessedAttrValue { value: None, .. } => {
|
2020-01-07 19:40:06 -05:00
|
|
|
// Value is empty, which is equivalent to no value, so discard `=`.
|
2020-01-25 07:05:07 -05:00
|
|
|
debug_assert_eq!(after_name.written_count(proc), 1);
|
|
|
|
after_name.erase_written(proc);
|
2020-01-07 08:38:42 -05:00
|
|
|
(AttrType::NoValue, None)
|
|
|
|
}
|
2021-08-08 03:58:10 -04:00
|
|
|
ProcessedAttrValue {
|
|
|
|
delimiter: DelimiterType::Unquoted,
|
|
|
|
value,
|
|
|
|
} => (AttrType::Unquoted, value),
|
|
|
|
ProcessedAttrValue {
|
|
|
|
delimiter: DelimiterType::Double,
|
|
|
|
value,
|
|
|
|
}
|
|
|
|
| ProcessedAttrValue {
|
|
|
|
delimiter: DelimiterType::Single,
|
|
|
|
value,
|
|
|
|
} => (AttrType::Quoted, value),
|
2019-12-26 00:17:57 -05:00
|
|
|
}
|
|
|
|
}
|
2019-12-27 05:52:49 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(ProcessedAttr { name, typ, value })
|
2019-12-25 04:44:51 -05:00
|
|
|
}
|