Skip to content

Commit

Permalink
Fixing image masking issues (#689)
Browse files Browse the repository at this point in the history
* Fixing image masking issues
  • Loading branch information
AbdelrhmanMagdy authored Nov 5, 2024
1 parent 38bd604 commit f6eb23e
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 17 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.7.54",
"version": "0.7.55",
"npmClient": "yarn",
"useWorkspaces": true
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "clarity",
"private": true,
"version": "0.7.54",
"version": "0.7.55",
"repository": "https://github.com/microsoft/clarity.git",
"author": "Sarvesh Nagpal <[email protected]>",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-decode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-decode",
"version": "0.7.54",
"version": "0.7.55",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand All @@ -26,7 +26,7 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-js": "^0.7.54"
"clarity-js": "^0.7.55"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.0",
Expand Down
8 changes: 4 additions & 4 deletions packages/clarity-devtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-devtools",
"version": "0.7.54",
"version": "0.7.55",
"private": true,
"description": "Adds Clarity debugging support to browser devtools",
"author": "Microsoft Corp.",
Expand All @@ -24,9 +24,9 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-decode": "^0.7.54",
"clarity-js": "^0.7.54",
"clarity-visualize": "^0.7.54"
"clarity-decode": "^0.7.55",
"clarity-js": "^0.7.55",
"clarity-visualize": "^0.7.55"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-devtools/static/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"manifest_version": 2,
"name": "Microsoft Clarity Developer Tools",
"description": "Clarity helps you understand how users are interacting with your website.",
"version": "0.7.54",
"version_name": "0.7.54",
"version": "0.7.55",
"version_name": "0.7.55",
"minimum_chrome_version": "50",
"devtools_page": "devtools.html",
"icons": {
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-js",
"version": "0.7.54",
"version": "0.7.55",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/src/core/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
let version = "0.7.54";
let version = "0.7.55";
export default version;
25 changes: 22 additions & 3 deletions packages/clarity-js/src/layout/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Privacy } from "@clarity-types/core";
import { Code, Setting, Severity } from "@clarity-types/data";
import { Constant, Mask, NodeInfo, NodeMeta, NodeValue, Selector, SelectorInput, Source } from "@clarity-types/layout";
import config from "@src/core/config";
import { bind } from "@src/core/event";
import hash from "@src/core/hash";
import { shortid } from "@src/data/metadata";
import * as internal from "@src/diagnostic/internal";
import * as region from "@src/layout/region";
import * as selector from "@src/layout/selector";
Expand Down Expand Up @@ -120,7 +122,7 @@ export function add(node: Node, parent: Node, data: NodeInfo, source: Source): v

privacy(node, values[id], parentValue);
updateSelector(values[id]);
size(values[id]);
updateImageSize(values[id]);
track(id, source);
}

Expand Down Expand Up @@ -250,6 +252,12 @@ function privacy(node: Node, value: NodeValue, parent: NodeValue): void {
// In a mode where we mask sensitive information by default, look through class names to aggressively mask content
metadata.privacy = inspect(attributes[Constant.Class], maskText, metadata);
break;
case tag === Constant.ImageTag:
// Mask images with blob src as it is not publicly available anyway.
if(attributes.src?.startsWith('blob:')){
metadata.privacy = Privacy.TextImage;
}
break;
}
}

Expand Down Expand Up @@ -361,10 +369,21 @@ function removeNodeFromNodesMap(id: number) {
}
}

function size(value: NodeValue): void {
function updateImageSize(value: NodeValue): void {
// If this element is a image node, and is masked, then track box model for the current element
if (value.data.tag === Constant.ImageTag && value.metadata.privacy === Privacy.TextImage) { value.metadata.size = []; }
if (value.data.tag === Constant.ImageTag && value.metadata.privacy === Privacy.TextImage) {
let img = getNode(value.id) as HTMLImageElement;
// We will not capture the natural image dimensions until it loads.
if(img && (!img.complete || img.naturalWidth === 0)){
// This will trigger mutation to update the original width and height after image loads.
bind(img, 'load', () => {
img.setAttribute('data-clarity-loaded', `${shortid()}`);
})
}
value.metadata.size = [];
}
}

function getPreviousId(node: Node): number {
let id = null;

Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-visualize/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-visualize",
"version": "0.7.54",
"version": "0.7.55",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand All @@ -27,7 +27,7 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-decode": "^0.7.54"
"clarity-decode": "^0.7.55"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.0",
Expand Down
20 changes: 20 additions & 0 deletions packages/clarity-visualize/src/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Layout as DecodedLayout } from "clarity-decode";
import { Asset, Constant, LinkHandler, NodeType, PlaybackState, Setting } from "@clarity-types/visualize";
import { StyleSheetOperation } from "clarity-js/types/layout";
import { AnimationOperation } from "clarity-js/types/layout";
import { Constant as LayoutConstants } from "clarity-js/types/layout";

export class LayoutHelper {
static TIMEOUT = 3000;
Expand All @@ -19,6 +20,9 @@ export class LayoutHelper {
animations = {};
state: PlaybackState = null;
stylesToApply: { [id: string] : string[] } = {};
BackgroundImageEligibleElements = ['DIV', 'SECTION', 'ARTICLE', 'HEADER', 'FOOTER', 'ASIDE', 'NAV', 'SPAN', 'P', 'MAIN'];
MaskedBackgroundImageStyle = `#CCC no-repeat center url("${Asset.Hide}")`;


constructor(state: PlaybackState, isMobile: boolean = false) {
this.state = state;
Expand Down Expand Up @@ -422,13 +426,29 @@ export class LayoutHelper {
}
return child;
}


// Mask images within a masked ancestor element in the node has a background image.
private mask = (node: HTMLElement) => {
if (node && this.BackgroundImageEligibleElements.includes(node.nodeName) && 'getComputedStyle' in window && 'closest' in node) {
const urlPattern = /url\(['"]?([^'")]+)['"]?\)/;
const computedStyles = window.getComputedStyle(node);
const hasBackgroundImage = computedStyles.backgroundImage?.match(urlPattern) || computedStyles.background?.match(urlPattern);
const masked = node.closest?.(`[${LayoutConstants.MaskData}]`);

if (hasBackgroundImage && masked) {
node.style.background = this.MaskedBackgroundImageStyle;
}
}
};

private insertBefore = (data: DecodedLayout.DomData, parent: Node, node: Node, next: Node): void => {
if (parent !== null) {
// Compare against both parentNode and parentElement to ensure visualization works correctly for shadow DOMs
next = next && (next.parentElement !== parent && next.parentNode !== parent) ? null : next;
try {
parent.insertBefore(node, next);
this.mask(node as HTMLElement);
} catch (ex) {
console.warn("Node: " + node + " | Parent: " + parent + " | Data: " + JSON.stringify(data));
console.warn("Exception encountered while inserting node: " + ex);
Expand Down

0 comments on commit f6eb23e

Please sign in to comment.