Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
2 changes: 1 addition & 1 deletion js-api/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion js-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "lol-html"
description = "Streaming HTML rewriter/parser with CSS selector-based API"
license = "BSD-3-Clause"
version = "2.1.1"
version = "2.2.0"
authors = ["Ivan Nikulin <inikulin@cloudflare.com>", "Gus Caplan <me@gus.host>"]
repository = "https://github.com/cloudflare/lol-html"
edition = "2021"
Expand Down
4 changes: 4 additions & 0 deletions js-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ rewriter.on('a[href]', {
.getAttribute('href')
.replace('http:', 'https:');
el.setAttribute('href', href);

el.onEndTag((tag)=> {
console.log(`Tag ended: ${tag.name}`);
});
},
});

Expand Down
34 changes: 34 additions & 0 deletions js-api/src/element.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
use super::end_tag::EndTag;
use super::*;
use js_sys::Function as JsFunction;
use lol_html::html_content::{Attribute as NativeAttribute, Element as NativeElement};
use serde::Serialize;
use serde_wasm_bindgen::to_value as to_js_value;
use thiserror::Error;

#[derive(Error, Debug)]
#[error("JS handler error")]
pub struct HandlerJsErrorWrap(pub JsValue);

// SAFETY: The exposed js-api only supports single-threaded usage.
unsafe impl Send for HandlerJsErrorWrap {}
unsafe impl Sync for HandlerJsErrorWrap {}

macro_rules! make_handler {
($handler:ident, $JsArgType:ident, $typehint:ty) => {{
fn type_hint(h: $typehint) -> $typehint {
h
}
type_hint(Box::new(move |arg: &mut _| {
$JsArgType::with_native(arg, |js_value| $handler.call1(&JsValue::NULL, &js_value))
.map_err(|e| HandlerJsErrorWrap(e))?;

Ok(())
}))
}};
}

#[derive(Serialize)]
pub struct Attribute {
Expand Down Expand Up @@ -112,4 +137,13 @@ impl Element {
pub fn remove_and_keep_content(&mut self) -> Result<(), JsValue> {
self.0.get_mut().map(|e| e.remove_and_keep_content())
}

#[wasm_bindgen(method, js_name=onEndTag)]
pub fn on_end_tag(&mut self, handler: JsFunction) -> JsResult<()> {
if let Some(handlers) = self.0.get_mut()?.end_tag_handlers() {
handlers.push(make_handler!(handler, EndTag, lol_html::EndTagHandler));
}

Ok(())
}
}
16 changes: 16 additions & 0 deletions js-api/src/end_tag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::*;
use lol_html::html_content::EndTag as NativeEndTag;

#[wasm_bindgen]
pub struct EndTag(NativeRefWrap<NativeEndTag<'static>>);

impl_from_native!(NativeEndTag => EndTag);
impl_mutations_end_tag!(EndTag);

#[wasm_bindgen]
impl EndTag {
#[wasm_bindgen(getter)]
pub fn name(&self) -> JsResult<String> {
self.0.get().map(|d| d.name())
}
}
42 changes: 42 additions & 0 deletions js-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,47 @@ macro_rules! impl_mutations {
};
}

macro_rules! impl_mutations_end_tag {
($Ty:ident) => {
#[wasm_bindgen]
impl $Ty {
pub fn before(
&mut self,
content: &str,
content_type: Option<ContentTypeOptions>,
) -> Result<(), JsValue> {
self.0
.get_mut()
.map(|o| o.before(content, content_type.into_native()))
}

pub fn after(
&mut self,
content: &str,
content_type: Option<ContentTypeOptions>,
) -> Result<(), JsValue> {
self.0
.get_mut()
.map(|o| o.after(content, content_type.into_native()))
}

pub fn replace(
&mut self,
content: &str,
content_type: Option<ContentTypeOptions>,
) -> Result<(), JsValue> {
self.0
.get_mut()
.map(|o| o.replace(content, content_type.into_native()))
}

pub fn remove(&mut self) -> Result<(), JsValue> {
self.0.get_mut().map(|o| o.remove())
}
}
};
}

macro_rules! impl_from_native {
($Ty:ty => $JsTy:path) => {
impl $JsTy {
Expand All @@ -171,6 +212,7 @@ mod comment;
mod doctype;
mod document_end;
mod element;
mod end_tag;
mod handlers;
mod html_rewriter;
mod text_chunk;
9 changes: 9 additions & 0 deletions js-api/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ const rewriter = new HTMLRewriter('utf8', (chunk) => {
chunks.push(chunk);
});

const endTags = [];
rewriter.on('a[href]', {
element(el) {
const href = el
.getAttribute('href')
.replace('http:', 'https:');
el.setAttribute('href', href);

el.onEndTag((tag) => {
endTags.push(tag.name);
});
},
});

Expand All @@ -30,3 +35,7 @@ const output = Buffer.concat(chunks).toString('utf8');
if (output != '<div><a href="https://example.com"></a></div>') {
throw "fail";
}

if (endTags.length != 1 || endTags[0] != 'a') {
throw "onEndTag fail";
}