From 852a9d67aa8bbdf8ee9964308ff9424fdf0624c0 Mon Sep 17 00:00:00 2001 From: Zeke Lu Date: Tue, 8 Jun 2021 18:07:10 +0800 Subject: [PATCH] load js source code with //go:embed --- js.go | 149 +++++------------------------ js/attribute.js | 3 + js/blur.js | 4 + js/reset.js | 10 ++ js/setAttribute.js | 8 ++ js/submit.js | 10 ++ js/text.js | 6 ++ js/textContent.js | 3 + js/visible.js | 3 + js/waitForPredicatePageFunction.js | 83 ++++++++++++++++ 10 files changed, 153 insertions(+), 126 deletions(-) create mode 100644 js/attribute.js create mode 100644 js/blur.js create mode 100644 js/reset.js create mode 100644 js/setAttribute.js create mode 100644 js/submit.js create mode 100644 js/text.js create mode 100644 js/textContent.js create mode 100644 js/visible.js create mode 100644 js/waitForPredicatePageFunction.js diff --git a/js.go b/js.go index 39b5b50f..a2ca228b 100644 --- a/js.go +++ b/js.go @@ -1,157 +1,54 @@ package chromedp -const ( +import ( + _ "embed" +) + +var ( // textJS is a javascript snippet that returns the innerText of the specified // visible (ie, offsetWidth || offsetHeight || getClientRects().length ) element. - textJS = `function text() { - if (this.offsetWidth || this.offsetHeight || this.getClientRects().length) { - return this.innerText; - } - return ''; - }` + //go:embed js/text.js + textJS string // textContentJS is a javascript snippet that returns the textContent of the // specified element. - textContentJS = `function textContent() { - return this.textContent; - }` + //go:embed js/textContent.js + textContentJS string // blurJS is a javascript snippet that blurs the specified element. - blurJS = `function blur() { - this.blur(); - return true; - }` + //go:embed js/blur.js + blurJS string // submitJS is a javascript snippet that will call the containing form's // submit function, returning true or false if the call was successful. - submitJS = `function submit() { - if (this.nodeName === 'FORM') { - HTMLFormElement.prototype.submit.call(this); - return true; - } else if (this.form !== null) { - HTMLFormElement.prototype.submit.call(this.form); - return true; - } - return false; - }` + //go:embed js/submit.js + submitJS string // resetJS is a javascript snippet that will call the containing form's // reset function, returning true or false if the call was successful. - resetJS = `function reset() { - if (this.nodeName === 'FORM') { - HTMLFormElement.prototype.reset.call(this); - return true; - } else if (this.form !== null) { - HTMLFormElement.prototype.reset.call(this.form); - return true; - } - return false; - }` + //go:embed js/reset.js + resetJS string // attributeJS is a javascript snippet that returns the attribute of a specified // node. - attributeJS = `function attribute(n) { - return this[n]; - }` + //go:embed js/attribute.js + attributeJS string // setAttributeJS is a javascript snippet that sets the value of the specified // node, and returns the value. - setAttributeJS = `function setAttribute(n, v) { - this[n] = v; - if (n === 'value') { - this.dispatchEvent(new Event('input', { bubbles: true })); - this.dispatchEvent(new Event('change', { bubbles: true })); - } - return this[n]; - }` + //go:embed js/setAttribute.js + setAttributeJS string // visibleJS is a javascript snippet that returns true or false depending on if // the specified node's offsetWidth, offsetHeight or getClientRects().length is // not null. - visibleJS = `function visible() { - return Boolean( this.offsetWidth || this.offsetHeight || this.getClientRects().length ); - }` + //go:embed js/visible.js + visibleJS string // waitForPredicatePageFunction is a javascript snippet that runs the polling in the // browser. It's copied from puppeteer. See // https://github.com/puppeteer/puppeteer/blob/669f04a7a6e96cc8353a8cb152898edbc25e7c15/src/common/DOMWorld.ts#L870-L944 // It's modified to make mutation polling respect timeout even when there is not DOM mutation. - waitForPredicatePageFunction = `async function waitForPredicatePageFunction(predicateBody, polling, timeout, ...args) { - const predicate = new Function('...args', predicateBody); - let timedOut = false; - if (timeout) - setTimeout(() => (timedOut = true), timeout); - if (polling === 'raf') - return await pollRaf(); - if (polling === 'mutation') - return await pollMutation(); - if (typeof polling === 'number') - return await pollInterval(polling); - /** - * @returns {!Promise<*>} - */ - async function pollMutation() { - const success = await predicate(...args); - if (success) - return Promise.resolve(success); - let fulfill; - const result = new Promise((x) => (fulfill = x)); - const observer = new MutationObserver(async () => { - if (timedOut) { - observer.disconnect(); - fulfill(); - } - const success = await predicate(...args); - if (success) { - observer.disconnect(); - fulfill(success); - } - }); - observer.observe(document, { - childList: true, - subtree: true, - attributes: true, - }); - if (timeout) - setTimeout(() => { - observer.disconnect(); - fulfill(); - }, timeout); - return result; - } - async function pollRaf() { - let fulfill; - const result = new Promise((x) => (fulfill = x)); - await onRaf(); - return result; - async function onRaf() { - if (timedOut) { - fulfill(); - return; - } - const success = await predicate(...args); - if (success) - fulfill(success); - else - requestAnimationFrame(onRaf); - } - } - async function pollInterval(pollInterval) { - let fulfill; - const result = new Promise((x) => (fulfill = x)); - await onTimeout(); - return result; - async function onTimeout() { - if (timedOut) { - fulfill(); - return; - } - const success = await predicate(...args); - if (success) - fulfill(success); - else - setTimeout(onTimeout, pollInterval); - } - } - }` + //go:embed js/waitForPredicatePageFunction.js + waitForPredicatePageFunction string ) diff --git a/js/attribute.js b/js/attribute.js new file mode 100644 index 00000000..5d0f8dd9 --- /dev/null +++ b/js/attribute.js @@ -0,0 +1,3 @@ +function attribute(n) { + return this[n]; +} diff --git a/js/blur.js b/js/blur.js new file mode 100644 index 00000000..fe1f6a23 --- /dev/null +++ b/js/blur.js @@ -0,0 +1,4 @@ +function blur() { + this.blur(); + return true; +} diff --git a/js/reset.js b/js/reset.js new file mode 100644 index 00000000..c6cf0c44 --- /dev/null +++ b/js/reset.js @@ -0,0 +1,10 @@ +function reset() { + if (this.nodeName === 'FORM') { + HTMLFormElement.prototype.reset.call(this); + return true; + } else if (this.form !== null) { + HTMLFormElement.prototype.reset.call(this.form); + return true; + } + return false; +} diff --git a/js/setAttribute.js b/js/setAttribute.js new file mode 100644 index 00000000..b8fa9cfb --- /dev/null +++ b/js/setAttribute.js @@ -0,0 +1,8 @@ +function setAttribute(n, v) { + this[n] = v; + if (n === 'value') { + this.dispatchEvent(new Event('input', {bubbles: true})); + this.dispatchEvent(new Event('change', {bubbles: true})); + } + return this[n]; +} diff --git a/js/submit.js b/js/submit.js new file mode 100644 index 00000000..2cea7ba4 --- /dev/null +++ b/js/submit.js @@ -0,0 +1,10 @@ +function submit() { + if (this.nodeName === 'FORM') { + HTMLFormElement.prototype.submit.call(this); + return true; + } else if (this.form !== null) { + HTMLFormElement.prototype.submit.call(this.form); + return true; + } + return false; +} diff --git a/js/text.js b/js/text.js new file mode 100644 index 00000000..6a6ada96 --- /dev/null +++ b/js/text.js @@ -0,0 +1,6 @@ +function text() { + if (this.offsetWidth || this.offsetHeight || this.getClientRects().length) { + return this.innerText; + } + return ''; +} diff --git a/js/textContent.js b/js/textContent.js new file mode 100644 index 00000000..1388af65 --- /dev/null +++ b/js/textContent.js @@ -0,0 +1,3 @@ +function textContent() { + return this.textContent; +} diff --git a/js/visible.js b/js/visible.js new file mode 100644 index 00000000..9abea8e7 --- /dev/null +++ b/js/visible.js @@ -0,0 +1,3 @@ +function visible() { + return Boolean(this.offsetWidth || this.offsetHeight || this.getClientRects().length); +} diff --git a/js/waitForPredicatePageFunction.js b/js/waitForPredicatePageFunction.js new file mode 100644 index 00000000..2b87d0e6 --- /dev/null +++ b/js/waitForPredicatePageFunction.js @@ -0,0 +1,83 @@ +async function waitForPredicatePageFunction(predicateBody, polling, timeout, ...args) { + const predicate = new Function('...args', predicateBody); + let timedOut = false; + if (timeout) + setTimeout(() => (timedOut = true), timeout); + if (polling === 'raf') + return await pollRaf(); + if (polling === 'mutation') + return await pollMutation(); + if (typeof polling === 'number') + return await pollInterval(polling); + + /** + * @returns {!Promise<*>} + */ + async function pollMutation() { + const success = await predicate(...args); + if (success) + return Promise.resolve(success); + let fulfill; + const result = new Promise((x) => (fulfill = x)); + const observer = new MutationObserver(async () => { + if (timedOut) { + observer.disconnect(); + fulfill(); + } + const success = await predicate(...args); + if (success) { + observer.disconnect(); + fulfill(success); + } + }); + observer.observe(document, { + childList: true, + subtree: true, + attributes: true, + }); + if (timeout) + setTimeout(() => { + observer.disconnect(); + fulfill(); + }, timeout); + return result; + } + + async function pollRaf() { + let fulfill; + const result = new Promise((x) => (fulfill = x)); + await onRaf(); + return result; + + async function onRaf() { + if (timedOut) { + fulfill(); + return; + } + const success = await predicate(...args); + if (success) + fulfill(success); + else + requestAnimationFrame(onRaf); + } + } + + async function pollInterval(pollInterval) { + let fulfill; + const result = new Promise((x) => (fulfill = x)); + await onTimeout(); + return result; + + async function onTimeout() { + if (timedOut) { + fulfill(); + return; + } + const success = await predicate(...args); + if (success) + fulfill(success); + else + setTimeout(onTimeout, pollInterval); + } + } +}