New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@applitools/dom-capture

Package Overview
Dependencies
Maintainers
12
Versions
98
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@applitools/dom-capture - npm Package Compare versions

Comparing version 3.1.5 to 4.0.1

src/captureNodeCss.js

384

dist/captureDom.js
function(props={"styleProps":["background-color","background-image","background-size","color","border-width","border-color","border-style","padding","margin"],"rectProps":["width","height","top","left","bottom","right"],"ignoredTagNames":["HEAD"]}, doc=document) {
(function() {
var captureDom = (function () {
'use strict';
function absolutizeUrl(url, absoluteUrl) {
return new URL(url, absoluteUrl).href;
}
var absolutizeUrl_1 = absolutizeUrl;
async function getBundledCssFromCssText(cssText, resourceUrl) {
try {
let bundledCss = '';
const styleSheet = parseCss(cssText);
for (const rule of Array.from(styleSheet.cssRules)) {
if (rule instanceof CSSImportRule) {
const nestedUrl = absolutizeUrl_1(rule.href, resourceUrl);
const nestedResource = await fetchCss(nestedUrl);
if (nestedResource) {
const nestedCssText = await getBundledCssFromCssText(
nestedResource.content.toString(),
nestedUrl,
);
bundledCss = `${nestedCssText}${bundledCss}`;
}
}
}
return `${bundledCss}${getCss(cssText, resourceUrl)}`;
} catch (ex) {}
}
async function fetchCss(url) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.text();
}
return '/failed to fetch (status ' + response.status + ') css from: ' + url + '/';
} catch (err) {
return '/failed to fetch (error ' + err.toString() + ') css from: ' + url + '/';
}
}
function parseCss(styleContent) {
var doc = document.implementation.createHTMLDocument(''),
styleElement = doc.createElement('style');
styleElement.textContent = styleContent;
// the style will only be parsed once it is added to a document
doc.body.appendChild(styleElement);
return styleElement.sheet;
}
function getCss(newText, url) {
return `\n/** ${url} **/\n${newText}`;
}
function isStyleElement(node) {
return node.nodeName && node.nodeName.toUpperCase() === 'STYLE';
}
function getHrefAttr(node) {
const attr = Array.from(node.attributes).find(attr => attr.name.toLowerCase() === 'href');
return attr && attr.value;
}
function isLinkToStyleSheet(node) {
return (
node.nodeName &&
node.nodeName.toUpperCase() === 'LINK' &&
node.attributes &&
Array.from(node.attributes).find(
attr => attr.name.toLowerCase() === 'rel' && attr.value.toLowerCase() === 'stylesheet',
)
);
}
async function captureNodeCss(baseUrl, node) {
let bundledCss = '';
let cssText, resourceUrl;
if (isStyleElement(node)) {
cssText = Array.from(node.childNodes)
.map(node => node.nodeValue)
.join('');
resourceUrl = baseUrl;
} else if (isLinkToStyleSheet(node)) {
resourceUrl = absolutizeUrl_1(getHrefAttr(node), baseUrl);
const resource = await fetchCss(resourceUrl);
if (resource) {
cssText = resource.toString();
}
}
if (cssText) {
const moreCss = await getBundledCssFromCssText(cssText, resourceUrl);
bundledCss = `${bundledCss}${moreCss}`;
}
for (const subNode of node.childNodes) {
const nodeCss = await captureNodeCss(baseUrl, subNode);
bundledCss += nodeCss;
}
return bundledCss;
}
async function captureFrameCss(url, doc) {
return await captureNodeCss(url, doc.documentElement);
}
var captureFrameCss_1 = captureFrameCss;
// TODO conditionally add/remove properties based on value (need to account for cross browser values)
const styleProps = [
'background-repeat',
'background-origin',
'background-position',
'background-color',
'background-image',
'background-size',
'color',
'border-width',
'border-color',
'border-style',
'color',
'display',
'font-size',
'line-height',
'margin',
'opacity',
'padding',
'margin',
'visibility',
];
const rectProps = [
'width',
'height',
'top',
'left',
'bottom', // TODO is this needed given that we have top+height ?
'right', // TODO is this needed given that we have left+width ?
];
const rectProps = ['width', 'height', 'top', 'left'];
const ignoredTagNames = ['HEAD'];
const ignoredTagNames = ['HEAD', 'SCRIPT'];

@@ -183,2 +75,185 @@ var defaultDomProps = {

function genXpath(el) {
if (!el.ownerDocument) return ''; // this is the document node
let xpath = '',
currEl = el,
doc = el.ownerDocument,
frameElement = doc.defaultView.frameElement;
while (currEl !== doc) {
xpath = `${currEl.tagName}[${getIndex(currEl)}]/${xpath}`;
currEl = currEl.parentNode;
}
if (frameElement) {
xpath = `${genXpath(frameElement)},${xpath}`;
}
return xpath.replace(/\/$/, '');
}
function getIndex(el) {
return (
Array.prototype.filter
.call(el.parentNode.childNodes, node => node.tagName === el.tagName)
.indexOf(el) + 1
);
}
var genXpath_1 = genXpath;
function absolutizeUrl(url, absoluteUrl) {
return new URL(url, absoluteUrl).href;
}
var absolutizeUrl_1 = absolutizeUrl;
function makeGetBundledCssFromCssText({
parseCss,
CSSImportRule,
absolutizeUrl,
fetchCss,
unfetchedToken,
}) {
return async function getBundledCssFromCssText(cssText, resourceUrl) {
let unfetchedResources;
let bundledCss = '';
try {
const styleSheet = parseCss(cssText);
for (const rule of Array.from(styleSheet.cssRules)) {
if (rule instanceof CSSImportRule) {
const nestedUrl = absolutizeUrl(rule.href, resourceUrl);
const nestedResource = await fetchCss(nestedUrl);
if (nestedResource !== undefined) {
const {
bundledCss: nestedCssText,
unfetchedResources: nestedUnfetchedResources,
} = await getBundledCssFromCssText(nestedResource, nestedUrl);
nestedUnfetchedResources && (unfetchedResources = new Set(nestedUnfetchedResources));
bundledCss = `${nestedCssText}${bundledCss}`;
} else {
unfetchedResources = new Set([nestedUrl]);
bundledCss = `\n${unfetchedToken}${nestedUrl}${unfetchedToken}`;
}
}
}
} catch (ex) {
console.log(`error during getBundledCssFromCssText, resourceUrl=${resourceUrl}`, ex);
}
bundledCss = `${bundledCss}${getCss(cssText, resourceUrl)}`;
return {
bundledCss,
unfetchedResources,
};
};
}
function getCss(newText, url) {
return `\n/** ${url} **/\n${newText}`;
}
var getBundledCssFromCssText = makeGetBundledCssFromCssText;
/* global document*/
function parseCss(styleContent) {
var doc = document.implementation.createHTMLDocument(''),
styleElement = doc.createElement('style');
styleElement.textContent = styleContent;
// the style will only be parsed once it is added to a document
doc.body.appendChild(styleElement);
return styleElement.sheet;
}
var parseCss_1 = parseCss;
function makeFetchCss(fetch) {
return async function fetchCss(url) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.text();
}
console.log('/failed to fetch (status ' + response.status + ') css from: ' + url + '/');
} catch (err) {
console.log('/failed to fetch (error ' + err.toString() + ') css from: ' + url + '/');
}
};
}
var fetchCss = makeFetchCss;
function makeExtractCssFromNode({fetchCss, absolutizeUrl}) {
return async function extractCssFromNode(node, baseUrl) {
let cssText, resourceUrl, isUnfetched;
if (isStyleElement(node)) {
cssText = Array.from(node.childNodes)
.map(node => node.nodeValue)
.join('');
resourceUrl = baseUrl;
} else if (isLinkToStyleSheet(node)) {
resourceUrl = absolutizeUrl(getHrefAttr(node), baseUrl);
cssText = await fetchCss(resourceUrl);
if (cssText === undefined) {
isUnfetched = true;
}
}
return {cssText, resourceUrl, isUnfetched};
};
}
function isStyleElement(node) {
return node.nodeName && node.nodeName.toUpperCase() === 'STYLE';
}
function getHrefAttr(node) {
const attr = Array.from(node.attributes).find(attr => attr.name.toLowerCase() === 'href');
return attr && attr.value;
}
function isLinkToStyleSheet(node) {
return (
node.nodeName &&
node.nodeName.toUpperCase() === 'LINK' &&
node.attributes &&
Array.from(node.attributes).find(
attr => attr.name.toLowerCase() === 'rel' && attr.value.toLowerCase() === 'stylesheet',
)
);
}
var extractCssFromNode = makeExtractCssFromNode;
function makeCaptureNodeCss({extractCssFromNode, getBundledCssFromCssText, unfetchedToken}) {
return async function captureNodeCss(node, baseUrl) {
const {resourceUrl, cssText, isUnfetched} = await extractCssFromNode(node, baseUrl);
let unfetchedResources;
let bundledCss = '';
if (cssText) {
const {
bundledCss: nestedCss,
unfetchedResources: nestedUnfetched,
} = await getBundledCssFromCssText(cssText, resourceUrl);
bundledCss += nestedCss;
unfetchedResources = new Set(nestedUnfetched);
} else if (isUnfetched) {
bundledCss += `${unfetchedToken}${resourceUrl}${unfetchedToken}`;
unfetchedResources = new Set([resourceUrl]);
}
return {bundledCss, unfetchedResources};
};
}
var captureNodeCss = makeCaptureNodeCss;
const NODE_TYPES = {
ELEMENT: 1,
TEXT: 3,
};
async function captureFrame(

@@ -188,7 +263,33 @@ {styleProps, rectProps, ignoredTagNames} = defaultDomProps,

) {
const NODE_TYPES = {
ELEMENT: 1,
TEXT: 3,
};
const start = Date.now();
const unfetchedResources = new Set();
const iframeCors = [];
const iframeToken = '@@@@@';
const unfetchedToken = '#####';
const separator = '-----';
const fetchCss$$1 = fetchCss(fetch);
const getBundledCssFromCssText$$1 = getBundledCssFromCssText({
parseCss: parseCss_1,
CSSImportRule,
fetchCss: fetchCss$$1,
absolutizeUrl: absolutizeUrl_1,
unfetchedToken,
});
const extractCssFromNode$$1 = extractCssFromNode({fetchCss: fetchCss$$1, absolutizeUrl: absolutizeUrl_1});
const captureNodeCss$$1 = captureNodeCss({
extractCssFromNode: extractCssFromNode$$1,
getBundledCssFromCssText: getBundledCssFromCssText$$1,
unfetchedToken,
});
const ret = await doCaptureFrame(doc);
const iframePrefix = iframeCors.length ? `${iframeToken}\n${iframeCors.join('\n')}\n` : '';
const unfetchedPrefix = unfetchedResources.size
? `${unfetchedToken}\n${Array.from(unfetchedResources).join('\n')}\n`
: '';
const prefix =
unfetchedPrefix || iframePrefix ? `${unfetchedPrefix}${iframePrefix}${separator}\n` : '';
console.log('[captureFrame]', Date.now() - start);
return `${prefix}${JSON.stringify(ret)}`;
function filter(x) {

@@ -211,4 +312,5 @@ return !!x;

const bgImages = new Set();
let bundledCss = '';
const ret = await captureNode(frameDoc.documentElement);
ret.css = await captureFrameCss_1(window.location, frameDoc);
ret.css = bundledCss;
ret.images = await getImageSizes_1({bgImages});

@@ -218,2 +320,9 @@ return ret;

async function captureNode(node) {
const {bundledCss: nodeCss, unfetchedResources: nodeUnfetched} = await captureNodeCss$$1(
node,
frameDoc.location.href,
);
bundledCss += nodeCss;
if (nodeUnfetched) for (const elem of nodeUnfetched) unfetchedResources.add(elem);
switch (node.nodeType) {

@@ -235,4 +344,9 @@ case NODE_TYPES.TEXT:

async function elementToJSON(el) {
const childNodes = (await Promise.all(
Array.prototype.map.call(el.childNodes, captureNode),
)).filter(filter);
const tagName = el.tagName.toUpperCase();
if (ignoredTagNames.indexOf(tagName) > -1) return null;
const computedStyle = window.getComputedStyle(el);

@@ -264,5 +378,3 @@ const boundingClientRect = el.getBoundingClientRect();

attributes: notEmptyObj(attributes),
childNodes: (await Promise.all(
Array.prototype.map.call(el.childNodes, captureNode),
)).filter(filter),
childNodes,
};

@@ -276,4 +388,7 @@ }

obj.childNodes = [await doCaptureFrame(el.contentDocument)];
} else {
const xpath = genXpath_1(el);
iframeCors.push(xpath);
obj.childNodes = [`${iframeToken}${xpath}${iframeToken}`];
}
} catch (ex) {
} finally {

@@ -284,7 +399,2 @@ return obj;

}
const start = Date.now();
const ret = await doCaptureFrame(doc);
console.log('[captureFrame]', Date.now() - start);
return ret;
}

@@ -298,3 +408,3 @@

return captureDom(props, doc);
}
return captureDom();
})()

@@ -1,10 +0,4 @@

const captureWindow = require('./src/captureWindow');
const captureFrame = require('./src/captureFrame');
const getCaptureDomScript = require('./src/getCaptureDomScript');
const defaultDomProps = require('./src/defaultDomProps');
module.exports = {
captureWindow,
captureFrame, // backward compatibility. Can probably be removed on December 2018.
getCaptureDomScript,
defaultDomProps,
};
{
"name": "@applitools/dom-capture",
"version": "3.1.5",
"version": "4.0.1",
"main": "index.js",

@@ -24,2 +24,3 @@ "license": "MIT",

"chai": "^4.2.0",
"cssom": "^0.3.4",
"eslint": "5.7.0",

@@ -30,2 +31,3 @@ "eslint-plugin-mocha-no-only": "1.0.0",

"express": "^4.16.4",
"jsdom": "^13.0.0",
"mocha": "5.2.0",

@@ -32,0 +34,0 @@ "prettier": "^1.14.3",

@@ -1,7 +0,17 @@

/* global window, document */
/* global window, document, CSSImportRule, fetch */
'use strict';
const captureFrameCss = require('./captureFrameCss');
const defaultDomProps = require('./defaultDomProps');
const getBackgroundImageUrl = require('./getBackgroundImageUrl');
const getImageSizes = require('./getImageSizes');
const genXpath = require('./genXpath');
const absolutizeUrl = require('./absolutizeUrl');
const makeGetBundledCssFromCssText = require('./getBundledCssFromCssText');
const parseCss = require('./parseCss');
const makeFetchCss = require('./fetchCss');
const makeExtractCssFromNode = require('./extractCssFromNode');
const makeCaptureNodeCss = require('./captureNodeCss');
const NODE_TYPES = {
ELEMENT: 1,
TEXT: 3,
};

@@ -12,7 +22,33 @@ async function captureFrame(

) {
const NODE_TYPES = {
ELEMENT: 1,
TEXT: 3,
};
const start = Date.now();
const unfetchedResources = new Set();
const iframeCors = [];
const iframeToken = '@@@@@';
const unfetchedToken = '#####';
const separator = '-----';
const fetchCss = makeFetchCss(fetch);
const getBundledCssFromCssText = makeGetBundledCssFromCssText({
parseCss,
CSSImportRule,
fetchCss,
absolutizeUrl,
unfetchedToken,
});
const extractCssFromNode = makeExtractCssFromNode({fetchCss, absolutizeUrl});
const captureNodeCss = makeCaptureNodeCss({
extractCssFromNode,
getBundledCssFromCssText,
unfetchedToken,
});
const ret = await doCaptureFrame(doc);
const iframePrefix = iframeCors.length ? `${iframeToken}\n${iframeCors.join('\n')}\n` : '';
const unfetchedPrefix = unfetchedResources.size
? `${unfetchedToken}\n${Array.from(unfetchedResources).join('\n')}\n`
: '';
const prefix =
unfetchedPrefix || iframePrefix ? `${unfetchedPrefix}${iframePrefix}${separator}\n` : '';
console.log('[captureFrame]', Date.now() - start);
return `${prefix}${JSON.stringify(ret)}`;
function filter(x) {

@@ -35,4 +71,5 @@ return !!x;

const bgImages = new Set();
let bundledCss = '';
const ret = await captureNode(frameDoc.documentElement);
ret.css = await captureFrameCss(window.location, frameDoc);
ret.css = bundledCss;
ret.images = await getImageSizes({bgImages});

@@ -42,2 +79,9 @@ return ret;

async function captureNode(node) {
const {bundledCss: nodeCss, unfetchedResources: nodeUnfetched} = await captureNodeCss(
node,
frameDoc.location.href,
);
bundledCss += nodeCss;
if (nodeUnfetched) for (const elem of nodeUnfetched) unfetchedResources.add(elem);
switch (node.nodeType) {

@@ -59,4 +103,9 @@ case NODE_TYPES.TEXT:

async function elementToJSON(el) {
const childNodes = (await Promise.all(
Array.prototype.map.call(el.childNodes, captureNode),
)).filter(filter);
const tagName = el.tagName.toUpperCase();
if (ignoredTagNames.indexOf(tagName) > -1) return null;
const computedStyle = window.getComputedStyle(el);

@@ -88,5 +137,3 @@ const boundingClientRect = el.getBoundingClientRect();

attributes: notEmptyObj(attributes),
childNodes: (await Promise.all(
Array.prototype.map.call(el.childNodes, captureNode),
)).filter(filter),
childNodes,
};

@@ -100,4 +147,7 @@ }

obj.childNodes = [await doCaptureFrame(el.contentDocument)];
} else {
const xpath = genXpath(el);
iframeCors.push(xpath);
obj.childNodes = [`${iframeToken}${xpath}${iframeToken}`];
}
} catch (ex) {
} finally {

@@ -108,9 +158,4 @@ return obj;

}
const start = Date.now();
const ret = await doCaptureFrame(doc);
console.log('[captureFrame]', Date.now() - start);
return ret;
}
module.exports = captureFrame;

@@ -1,24 +0,24 @@

// TODO conditionally add/remove properties based on value (need to account for cross browser values)
const styleProps = [
'background-repeat',
'background-origin',
'background-position',
'background-color',
'background-image',
'background-size',
'color',
'border-width',
'border-color',
'border-style',
'color',
'display',
'font-size',
'line-height',
'margin',
'opacity',
'padding',
'margin',
'visibility',
];
const rectProps = [
'width',
'height',
'top',
'left',
'bottom', // TODO is this needed given that we have top+height ?
'right', // TODO is this needed given that we have left+width ?
];
const rectProps = ['width', 'height', 'top', 'left'];
const ignoredTagNames = ['HEAD'];
const ignoredTagNames = ['HEAD', 'SCRIPT'];

@@ -25,0 +25,0 @@ module.exports = {

@@ -1,6 +0,26 @@

function genXpath(el, parent, parentXPath) {
const index = parent.childNodes.filter(node => node.tagName === el.tagName).indexOf(el) + 1;
return `${parentXPath}/${el.tagName}[${index}]`;
function genXpath(el) {
if (!el.ownerDocument) return ''; // this is the document node
let xpath = '',
currEl = el,
doc = el.ownerDocument,
frameElement = doc.defaultView.frameElement;
while (currEl !== doc) {
xpath = `${currEl.tagName}[${getIndex(currEl)}]/${xpath}`;
currEl = currEl.parentNode;
}
if (frameElement) {
xpath = `${genXpath(frameElement)},${xpath}`;
}
return xpath.replace(/\/$/, '');
}
function getIndex(el) {
return (
Array.prototype.filter
.call(el.parentNode.childNodes, node => node.tagName === el.tagName)
.indexOf(el) + 1
);
}
module.exports = genXpath;
'use strict';
const defaultDomProps = require('../src/defaultDomProps');
// This rollup plugin is meant to create a standalone function that can be consumed as a string and be sent to a remote browser for evaluation.

@@ -14,6 +12,6 @@ // I couldn't find a way in rollup to create an iife that runs the actual code (instead of returning a function that runs the code) without polluting the global scope

bundleFile.code = `
function(props=${JSON.stringify(defaultDomProps)}, doc=document) {
(function() {
${bundleFile.code}
return ${filename}(props, doc);
}`;
return ${filename}();
})()`;
},

@@ -20,0 +18,0 @@ };

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc