Skip to content

Commit

Permalink
fix: Do not rely on web components calling the license checker (#14028)
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur- committed Jun 23, 2022
1 parent 9e5175d commit 60a8728
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 49 deletions.
90 changes: 57 additions & 33 deletions flow-client/src/main/frontend/License.ts
Expand Up @@ -60,54 +60,63 @@ const showNoLicenseFallback = (element: Element, productAndMessage: ProductAndMe

const productTagNames: Record<string, string[]> = {};
const productMissingLicense: Record<string, ProductAndMessage> = {};
const productCheckOk: Record<string, boolean> = {};

/* eslint-disable func-names */
const overrideCustomElementsDefine = () => {
const { define } = window.customElements;

window.customElements.define = function (
tagName,
constructor: CustomElementConstructor & { cvdlName?: string },
options
) {
const { cvdlName } = constructor;
if (cvdlName) {
productTagNames[cvdlName] = productTagNames[cvdlName] ?? [];
productTagNames[cvdlName].push(tagName);

const productInfo = productMissingLicense[cvdlName];
if (productInfo) {
const { connectedCallback } = constructor.prototype;
constructor.prototype.connectedCallback = function () {
setTimeout(() => showNoLicenseFallback(this, productInfo), noLicenseFallbackTimeout);

if (connectedCallback) {
connectedCallback.call(this);
}
};
const key = (product: Product): string => {
return `${product.name}_${product.version}`;
};

const checkLicenseIfNeeded = (tagName: string) => {
const constructor: CustomElementConstructor & { cvdlName?: string } & { version?: string } = customElements.get(
tagName
)!;
const { cvdlName, version } = constructor;
if (cvdlName && version) {
const product: Product = { name: cvdlName, version };
productTagNames[cvdlName] = productTagNames[cvdlName] ?? [];
productTagNames[cvdlName].push(tagName);

const { connectedCallback } = constructor.prototype;
constructor.prototype.connectedCallback = function () {
const failedLicenseCheck = productMissingLicense[key(product)];
if (failedLicenseCheck) {
// Has been checked and the check failed
setTimeout(() => showNoLicenseFallback(this, failedLicenseCheck), noLicenseFallbackTimeout);
}
}
if (connectedCallback) {
connectedCallback.call(this);
}
};

define.call(this, tagName, constructor, options);
};
if (productMissingLicense[key(product)] || productCheckOk[key(product)]) {
// Already checked
} else {
// Has not been checked
(window as any).Vaadin.devTools.checkLicense(product);
}
}
};
/* eslint-enable func-names */

export const licenseCheckOk = (data: Product) => {
productCheckOk[key(data)] = true;

// eslint-disable-next-line no-console
console.debug('License check ok for ', data);
};

export const licenseCheckFailed = (data: ProductAndMessage) => {
const productName = data.product.name;
productMissingLicense[productName] = data;
productMissingLicense[key(data.product)] = data;
// eslint-disable-next-line no-console
console.error('License check failed for ', productName);

const tags = productTagNames[productName];
if (tags?.length > 0) {
findAll(document, tags).forEach((element) => {
setTimeout(() => showNoLicenseFallback(element, productMissingLicense[productName]), noLicenseFallbackTimeout);
setTimeout(
() => showNoLicenseFallback(element, productMissingLicense[key(data.product)]),
noLicenseFallbackTimeout
);
});
}
};
Expand All @@ -117,16 +126,31 @@ export const licenseCheckNoKey = (data: ProductAndMessage) => {

const productName = data.product.name;
data.messageHtml = `No license found. <a target=_blank onclick="javascript:window.open(this.href);return false;" href="${keyUrl}">Go here to start a trial or retrieve your license.</a>`;
productMissingLicense[productName] = data;
productMissingLicense[key(data.product)] = data;
// eslint-disable-next-line no-console
console.error('No license found when checking ', productName);

const tags = productTagNames[productName];
if (tags?.length > 0) {
findAll(document, tags).forEach((element) => {
setTimeout(() => showNoLicenseFallback(element, productMissingLicense[productName]), noLicenseFallbackTimeout);
setTimeout(
() => showNoLicenseFallback(element, productMissingLicense[key(data.product)]),
noLicenseFallbackTimeout
);
});
}
};

overrideCustomElementsDefine();
export const licenseInit = () => {
// Process already registered elements
(window as any).Vaadin.devTools.definedCustomElements.forEach((tagName: string) => {
checkLicenseIfNeeded(tagName);
});

// Handle new elements directly
(window as any).Vaadin.devTools.definedCustomElements = {
push: (tagName: string) => {
checkLicenseIfNeeded(tagName);
}
};
};
10 changes: 6 additions & 4 deletions flow-client/src/main/frontend/vaadin-dev-tools.ts
Expand Up @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { copy } from './copy-to-clipboard.js';
import { licenseCheckFailed, licenseCheckNoKey, licenseCheckOk, Product } from './License';
import { licenseCheckFailed, licenseCheckNoKey, licenseCheckOk, Product, licenseInit } from './License';

interface ServerInfo {
vaadinVersion: string;
Expand Down Expand Up @@ -1125,9 +1125,11 @@ export class VaadinDevTools extends LitElement {
10
);

if ((window as any).Vaadin) {
(window as any).Vaadin.devTools = this;
}
const windowAny = window as any;
windowAny.Vaadin = windowAny.Vaadin || {};
windowAny.Vaadin.devTools = Object.assign(this, windowAny.Vaadin.devTools);

licenseInit();
}
format(o: any): string {
return o.toString();
Expand Down
Expand Up @@ -78,6 +78,7 @@
import com.vaadin.flow.router.LocationUtil;
import com.vaadin.flow.router.QueryParameters;
import com.vaadin.flow.server.communication.AtmospherePushConnection;
import com.vaadin.flow.server.communication.IndexHtmlRequestHandler;
import com.vaadin.flow.server.communication.PushConnectionFactory;
import com.vaadin.flow.server.communication.UidlWriter;
import com.vaadin.flow.server.frontend.FrontendUtils;
Expand Down Expand Up @@ -759,6 +760,7 @@ public Document getBootstrapPage(BootstrapContext context) {
if (!config.isProductionMode()) {
UsageStatisticsExporter
.exportUsageStatisticsToDocument(document);
IndexHtmlRequestHandler.addLicenseChecker(document);
}

setupPwa(document, context);
Expand Down
Expand Up @@ -175,18 +175,32 @@ private void catchErrorsInDevMode(Document indexDocument) {
);
}

private void addLicenseChecker(Document indexDocument) {
/**
* Adds the needed overrides for the license checker to work when in
* development mode.
*/
public static void addLicenseChecker(Document indexDocument) {
// maybeCheck is invoked by the WC license checker
addScript(indexDocument, "" + //
"window.Vaadin = window.Vaadin || {};" + //
"window.Vaadin.VaadinLicenseChecker = {" + //
" maybeCheck: (productInfo) => {" + //
" window.Vaadin.devTools.checkLicense(productInfo);" + //
// This disables the license check that the web components are
// still using
" }" + //
"};" + //
"window.Vaadin.devTools = window.Vaadin.devTools || {};"
+ "window.Vaadin.devTools.definedCustomElements = window.Vaadin.devTools.definedCustomElements || [];"
+ //
"const { define } = window.customElements;" + //
"window.customElements.define = function (tagName, ...args) {" + //
"define.call(this, tagName, ...args);" + //
"window.Vaadin.devTools.definedCustomElements.push(tagName);" + //
"};");

}

private void addScript(Document indexDocument, String script) {
private static void addScript(Document indexDocument, String script) {
Element elm = new Element(SCRIPT);
elm.attr(SCRIPT_INITIAL, "");
elm.appendChild(new DataNode(script));
Expand Down
Expand Up @@ -599,7 +599,7 @@ public void page_configurator_inlines_javascript_from_content()
"Content javascript should have been prepended to head element",
"<script type=\"text/javascript\">window.messages = window.messages || [];\n"
+ "window.messages.push(\"content script\");</script>",
allElements.get(1).toString());
allElements.get(2).toString());
}

@Test // 3036
Expand All @@ -618,7 +618,7 @@ public void page_configurator_inlines_prepend_javascript_from_file()
"Content javascript should have been prepended to head element",
"<script type=\"text/javascript\">window.messages = window.messages || [];\n"
+ "window.messages.push(\"inline.js\");</script>",
allElements.get(1).toString());
allElements.get(2).toString());
}

@Test // 3036
Expand Down Expand Up @@ -687,7 +687,7 @@ public void page_configurator_adds_meta_tags()
Elements allElements = page.head().getAllElements();

Assert.assertEquals("<meta name=\"theme-color\" content=\"#227aef\">",
allElements.get(1).toString());
allElements.get(2).toString());
}

@Test // 3203
Expand Down Expand Up @@ -887,23 +887,23 @@ public void use_inline_to_prepend_files_to_head()
"File javascript should have been prepended to head element",
"<script type=\"text/javascript\">window.messages = window.messages || [];\n"
+ "window.messages.push(\"inline.js\");</script>",
allElements.get(1).toString());
allElements.get(2).toString());
assertStringEquals(
"File html should have been prepended to head element",
"<script type=\"text/javascript\">\n"
+ " // document.body might not yet be accessible, so just leave a message\n"
+ " window.messages = window.messages || [];\n"
+ " window.messages.push(\"inline.html\");\n"
+ "</script>",
allElements.get(2).toString());
allElements.get(3).toString());
assertStringEquals(
"File css should have been prepended to head element",
"<style type=\"text/css\">/* inline.css */\n" + "\n"
+ "#preloadedDiv {\n"
+ " color: rgba(255, 255, 0, 1);\n" + "}\n" + "\n"
+ "#inlineCssTestDiv {\n"
+ " color: rgba(255, 255, 0, 1);\n" + "}</style>",
allElements.get(3).toString());
allElements.get(4).toString());
}

@Test // 3010
Expand Down
Expand Up @@ -203,7 +203,6 @@ public void writeBootstrapPage_scriptGuardedAndDevToolsDisabled()
throws IOException, ServiceException {
TestWebComponentBootstrapHandler handler = new TestWebComponentBootstrapHandler();
VaadinServletService service = new MockVaadinServletService();

initLookup(service);

VaadinSession session = new MockVaadinSession(service);
Expand Down Expand Up @@ -232,8 +231,8 @@ public void writeBootstrapPage_scriptGuardedAndDevToolsDisabled()
"if (!hasScript(\"/VAADIN/build/vaadin-export-2222.cache.js\")) {");
Assert.assertTrue(guardIndex > scriptIndex);

int createScriptIndex = result
.indexOf("document.createElement('script')");
int createScriptIndex = result.indexOf(
"ocument.createElement('script');headElem.setAttribute('type'");
Assert.assertTrue(createScriptIndex > guardIndex);

Assert.assertTrue(result.contains("\\\"devToolsEnabled\\\": false"));
Expand Down

0 comments on commit 60a8728

Please sign in to comment.