Skip to content

Commit 3ddfbc5

Browse files
brianhallgithub-actions[bot]
authored andcommitted
Release build 8.14.0 [ci release]
1 parent d26d4a6 commit 3ddfbc5

File tree

27 files changed

+1040
-112
lines changed

27 files changed

+1040
-112
lines changed

CHANGELOG.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
- Setting of document for tracker analysis (#1613)
1+
- Add Image Captcha Solver (#1612)
2+
- Add Alex to CODEOWNERS for PIR (#1625)
3+
- Renamed message: thumbnail to dismiss (#1603)

CODEOWNERS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ injected/src/element-hiding.js @duckduckgo/content-scope-scripts-owners @jonatha
77
injected/src/features/click-to-load.js @duckduckgo/content-scope-scripts-owners @kzar @ladamski @franfaccin @jonathanKingston @shakyShane
88
injected/src/features/click-to-load/ @duckduckgo/content-scope-scripts-owners @kzar @ladamski @franfaccin @jonathanKingston @shakyShane
99
injected/src/locales/click-to-load/ @duckduckgo/content-scope-scripts-owners @kzar @ladamski @franfaccin @jonathanKingston @shakyShane
10-
injected/src/features/broker-protection.js @duckduckgo/content-scope-scripts-owners @brianhall @shakyShane
11-
injected/src/features/broker-protection/ @duckduckgo/content-scope-scripts-owners @brianhall @shakyShane
10+
injected/src/features/broker-protection.js @duckduckgo/content-scope-scripts-owners @brianhall @shakyShane @madblex
11+
injected/src/features/broker-protection/ @duckduckgo/content-scope-scripts-owners @brianhall @shakyShane @madblex
1212
injected/src/features/autofill-password-import.js @duckduckgo/content-scope-scripts-owners @dbajpeyi
1313

1414
# Platform owners

Sources/ContentScopeScripts/dist/contentScopeIsolated.js

Lines changed: 157 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3986,7 +3986,7 @@
39863986
* | {name: "play.use", remember: "0" | "1"}
39873987
* | {name: "play.use.thumbnail"}
39883988
* | {name: "play.do_not_use", remember: "0" | "1"}
3989-
* | {name: "play.do_not_use.thumbnail"}} input
3989+
* | {name: "play.do_not_use.dismiss"}} input
39903990
*/
39913991
constructor(input) {
39923992
this.input = input;
@@ -4004,7 +4004,7 @@
40044004
case "play.do_not_use": {
40054005
return { remember: this.input.remember };
40064006
}
4007-
case "play.do_not_use.thumbnail":
4007+
case "play.do_not_use.dismiss":
40084008
return {};
40094009
default:
40104010
throw new Error("unreachable");
@@ -5497,10 +5497,10 @@
54975497
return this.mobileOptOut(e.detail.remember).catch(console.error);
54985498
});
54995499
drawer.addEventListener(DDGVideoDrawerMobile.DISMISS, () => {
5500-
return this.mobileOptOut(false).catch(console.error);
5500+
return this.dismissOverlay();
55015501
});
55025502
drawer.addEventListener(DDGVideoDrawerMobile.THUMBNAIL_CLICK, () => {
5503-
return this.videoThumbnailClick();
5503+
return this.dismissOverlay();
55045504
});
55055505
drawer.addEventListener(DDGVideoDrawerMobile.OPT_IN, (e) => {
55065506
return this.mobileOptIn(e.detail.remember, params).catch(console.error);
@@ -5661,8 +5661,8 @@
56615661
}
56625662
this.destroy();
56635663
}
5664-
videoThumbnailClick() {
5665-
const pixel = new Pixel({ name: "play.do_not_use.thumbnail" });
5664+
dismissOverlay() {
5665+
const pixel = new Pixel({ name: "play.do_not_use.dismiss" });
56665666
this.messages.sendPixel(pixel);
56675667
return this.destroy();
56685668
}
@@ -8656,19 +8656,47 @@
86568656

86578657
// src/features/broker-protection/captcha-services/utils/token.js
86588658
init_define_import_meta_trackerLookup();
8659-
function injectTokenIntoElement({ captchaContainerElement, elementName, token }) {
8660-
const element = getElementByTagName(captchaContainerElement, elementName);
8659+
8660+
// src/features/broker-protection/captcha-services/utils/element.js
8661+
init_define_import_meta_trackerLookup();
8662+
function isElementType(element, tag) {
8663+
if (Array.isArray(tag)) {
8664+
return tag.some((t) => isElementType(element, t));
8665+
}
8666+
return element.tagName.toLowerCase() === tag.toLowerCase();
8667+
}
8668+
8669+
// src/features/broker-protection/captcha-services/utils/token.js
8670+
function injectTokenIntoElement({ captchaContainerElement, captchaInputElement, elementName, token }) {
8671+
let element;
8672+
if (captchaInputElement) {
8673+
element = captchaInputElement;
8674+
} else if (elementName) {
8675+
element = getElementByTagName(captchaContainerElement, elementName);
8676+
} else {
8677+
return PirError.create(`[injectTokenIntoElement] must pass in either captcha input element or element name`);
8678+
}
86618679
if (!element) {
8662-
return PirError.create(`[injectTokenIntoElement] could not find element with name ${elementName}`);
8680+
return PirError.create(`[injectTokenIntoElement] could not find element to inject token into`);
86638681
}
86648682
return safeCallWithError(
86658683
() => {
8666-
element.innerHTML = token;
8667-
return PirSuccess.create({ injected: true });
8684+
if (isInputElement(element) && ["text", "hidden"].includes(element.type) || isTextAreaElement(element)) {
8685+
element.value = token;
8686+
return PirSuccess.create({ injected: true });
8687+
} else {
8688+
return PirError.create(`[injectTokenIntoElement] element is neither a text input or textarea`);
8689+
}
86688690
},
8669-
{ errorMessage: `[injectTokenIntoElement] error injecting token into element ${elementName}` }
8691+
{ errorMessage: `[injectTokenIntoElement] error injecting token into element` }
86708692
);
86718693
}
8694+
function isInputElement(element) {
8695+
return isElementType(element, "input");
8696+
}
8697+
function isTextAreaElement(element) {
8698+
return isElementType(element, "textarea");
8699+
}
86728700

86738701
// src/features/broker-protection/actions/captcha-callback.js
86748702
init_define_import_meta_trackerLookup();
@@ -8749,9 +8777,11 @@
87498777
* @param {HTMLElement} captchaContainerElement
87508778
*/
87518779
getCaptchaIdentifier(captchaContainerElement) {
8752-
return safeCallWithError(
8753-
() => getSiteKeyFromSearchParam({ captchaElement: this._getCaptchaElement(captchaContainerElement), siteKeyAttrName: "k" }),
8754-
{ errorMessage: "[ReCaptchaProvider.getCaptchaIdentifier] could not extract site key" }
8780+
return Promise.resolve(
8781+
safeCallWithError(
8782+
() => getSiteKeyFromSearchParam({ captchaElement: this._getCaptchaElement(captchaContainerElement), siteKeyAttrName: "k" }),
8783+
{ errorMessage: "[ReCaptchaProvider.getCaptchaIdentifier] could not extract site key" }
8784+
)
87558785
);
87568786
}
87578787
getSupportingCodeToInject() {
@@ -8791,6 +8821,114 @@
87918821
};
87928822
_config = new WeakMap();
87938823

8824+
// src/features/broker-protection/captcha-services/providers/image.js
8825+
init_define_import_meta_trackerLookup();
8826+
8827+
// src/features/broker-protection/captcha-services/utils/image.js
8828+
init_define_import_meta_trackerLookup();
8829+
function svgToBase64Jpg(svgElement, backgroundColor = "white") {
8830+
const svgString = new XMLSerializer().serializeToString(svgElement);
8831+
const svgDataUrl = "data:image/svg+xml;base64," + btoa(svgString);
8832+
return new Promise((resolve, reject) => {
8833+
const img = new Image();
8834+
img.onload = () => {
8835+
const canvas = document.createElement("canvas");
8836+
const ctx = canvas.getContext("2d");
8837+
if (!ctx) {
8838+
reject(new Error("Could not get 2D context from canvas"));
8839+
return;
8840+
}
8841+
canvas.width = img.width;
8842+
canvas.height = img.height;
8843+
ctx.fillStyle = backgroundColor;
8844+
ctx.fillRect(0, 0, canvas.width, canvas.height);
8845+
ctx.drawImage(img, 0, 0);
8846+
const jpgBase64 = canvas.toDataURL("image/jpeg");
8847+
resolve(jpgBase64);
8848+
};
8849+
img.onerror = (error) => {
8850+
reject(error);
8851+
};
8852+
img.src = svgDataUrl;
8853+
});
8854+
}
8855+
function imageToBase64(imageElement) {
8856+
const canvas = document.createElement("canvas");
8857+
const ctx = canvas.getContext("2d");
8858+
if (!ctx) {
8859+
throw Error("[imageToBase64] Could not get 2D context from canvas");
8860+
}
8861+
canvas.width = imageElement.width;
8862+
canvas.height = imageElement.height;
8863+
ctx.drawImage(imageElement, 0, 0, canvas.width, canvas.height);
8864+
const base64String = canvas.toDataURL("image/jpeg");
8865+
return base64String;
8866+
}
8867+
8868+
// src/features/broker-protection/captcha-services/providers/image.js
8869+
var ImageProvider = class {
8870+
getType() {
8871+
return "image";
8872+
}
8873+
/**
8874+
* @param {HTMLElement} captchaImageElement - The captcha image element
8875+
*/
8876+
isSupportedForElement(captchaImageElement) {
8877+
if (!captchaImageElement) {
8878+
return false;
8879+
}
8880+
return isElementType(captchaImageElement, ["img", "svg"]);
8881+
}
8882+
/**
8883+
* @param {HTMLElement} captchaImageElement - The captcha image element
8884+
*/
8885+
async getCaptchaIdentifier(captchaImageElement) {
8886+
if (isSVGElement(captchaImageElement)) {
8887+
return await svgToBase64Jpg(captchaImageElement);
8888+
}
8889+
if (isImgElement(captchaImageElement)) {
8890+
return imageToBase64(captchaImageElement);
8891+
}
8892+
return PirError.create(
8893+
`[ImageProvider.getCaptchaIdentifier] could not extract Base64 from image with tag name: ${captchaImageElement.tagName}`
8894+
);
8895+
}
8896+
getSupportingCodeToInject() {
8897+
return null;
8898+
}
8899+
/**
8900+
* @param {HTMLElement} captchaInputElement - The captcha input element
8901+
*/
8902+
canSolve(captchaInputElement) {
8903+
return isElementType(captchaInputElement, ["input", "textarea"]);
8904+
}
8905+
/**
8906+
* @param {HTMLInputElement} captchaInputElement - The captcha input element
8907+
* @param {string} token - The solved captcha token
8908+
*/
8909+
injectToken(captchaInputElement, token) {
8910+
return injectTokenIntoElement({ captchaInputElement, token });
8911+
}
8912+
/**
8913+
* @param {HTMLElement} _captchaInputElement - The element containing the captcha
8914+
* @param {string} _token - The solved captcha token
8915+
*/
8916+
getSolveCallback(_captchaInputElement, _token) {
8917+
return stringifyFunction({
8918+
functionBody: function callbackNoop() {
8919+
},
8920+
functionName: "callbackNoop",
8921+
args: {}
8922+
});
8923+
}
8924+
};
8925+
function isSVGElement(element) {
8926+
return isElementType(element, "svg");
8927+
}
8928+
function isImgElement(element) {
8929+
return isElementType(element, "img");
8930+
}
8931+
87948932
// src/features/broker-protection/captcha-services/providers/registry.js
87958933
var captchaFactory = new CaptchaFactory();
87968934
captchaFactory.registerProvider(
@@ -8807,6 +8945,7 @@
88078945
responseElementName: "g-recaptcha-response"
88088946
})
88098947
);
8948+
captchaFactory.registerProvider(new ImageProvider());
88108949

88118950
// src/features/broker-protection/captcha-services/get-captcha-provider.js
88128951
function getCaptchaProvider(captchaContainer, captchaType) {
@@ -8955,7 +9094,7 @@
89559094
}
89569095
return SuccessResponse.create({ actionID, actionType, response: { code: captchaProvider.getSupportingCodeToInject() } });
89579096
}
8958-
function getCaptchaInfo2(action, root = document) {
9097+
async function getCaptchaInfo2(action, root = document) {
89599098
const { id: actionID, actionType, captchaType, selector } = action;
89609099
if (!captchaType) {
89619100
return getCaptchaInfo(action, root);
@@ -8969,7 +9108,7 @@
89699108
if (PirError.isError(captchaProvider)) {
89709109
return createError(captchaProvider.error.message);
89719110
}
8972-
const captchaIdentifier = captchaProvider.getCaptchaIdentifier(captchaContainer);
9111+
const captchaIdentifier = await captchaProvider.getCaptchaIdentifier(captchaContainer);
89739112
if (!captchaIdentifier) {
89749113
return createError(`could not extract captcha identifier from the container with selector ${selector}`);
89759114
}
@@ -9070,7 +9209,7 @@
90709209
case "fillForm":
90719210
return fillForm(action, data(action, inputData, "extractedProfile"), root);
90729211
case "getCaptchaInfo":
9073-
return getCaptchaInfo2(action, root);
9212+
return await getCaptchaInfo2(action, root);
90749213
case "solveCaptcha":
90759214
return solveCaptcha2(action, data(action, inputData, "token"), root);
90769215
default: {

0 commit comments

Comments
 (0)