Skip to content

Commit 7e42b4e

Browse files
Release build 11.8.0 [ci release]
1 parent 21f48ae commit 7e42b4e

File tree

56 files changed

+16949
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+16949
-101
lines changed

CHANGELOG.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
- Add additionalCheck for android rollout (#1908)
2-
- Ensure message correctness in extension (#1907)
3-
- Add @duckduckgo/extension-owners to CODEOWNERS (#1896)
4-
- Auto approve patch changes to the repo (#1904)
1+
- Add support for internal conditional matching (#1913)
2+
- adsjs messaging (#1883)
3+
- Add support for async values and function responses (#1916)
4+
- Add support for maxSupportedVersion (#1912)
5+
- Open up canShare some more (#1906)

build/android/adsjsContentScope.js

Lines changed: 5660 additions & 0 deletions
Large diffs are not rendered by default.

build/android/autofillPasswordImport.js

Lines changed: 301 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,11 @@
197197
switch (configSettingType) {
198198
case "object":
199199
if (Array.isArray(configSetting)) {
200-
configSetting = processAttrByCriteria(configSetting);
201-
if (configSetting === void 0) {
200+
const selectedSetting = processAttrByCriteria(configSetting);
201+
if (selectedSetting === void 0) {
202202
return defaultValue;
203203
}
204+
return processAttr(selectedSetting, defaultValue);
204205
}
205206
if (!configSetting.type) {
206207
return defaultValue;
@@ -209,10 +210,17 @@
209210
if (configSetting.functionName && functionMap[configSetting.functionName]) {
210211
return functionMap[configSetting.functionName];
211212
}
213+
if (configSetting.functionValue) {
214+
const functionValue = configSetting.functionValue;
215+
return () => processAttr(functionValue, void 0);
216+
}
212217
}
213218
if (configSetting.type === "undefined") {
214219
return void 0;
215220
}
221+
if (configSetting.async) {
222+
return DDGPromise.resolve(configSetting.value);
223+
}
216224
return configSetting.value;
217225
default:
218226
return defaultValue;
@@ -386,6 +394,18 @@
386394
}
387395
return false;
388396
}
397+
function isMaxSupportedVersion(maxSupportedVersion, currentVersion) {
398+
if (typeof currentVersion === "string" && typeof maxSupportedVersion === "string") {
399+
if (satisfiesMinVersion(currentVersion, maxSupportedVersion)) {
400+
return true;
401+
}
402+
} else if (typeof currentVersion === "number" && typeof maxSupportedVersion === "number") {
403+
if (maxSupportedVersion >= currentVersion) {
404+
return true;
405+
}
406+
}
407+
return false;
408+
}
389409
function processConfig(data, userList, preferences, platformSpecificFeatures2 = []) {
390410
const topLevelHostname = getTabHostname();
391411
const site = computeLimitedSiteObject();
@@ -530,6 +550,17 @@
530550
android: [...baseFeatures, "webCompat", "breakageReporting", "duckPlayer", "messageBridge"],
531551
"android-broker-protection": ["brokerProtection"],
532552
"android-autofill-password-import": ["autofillPasswordImport"],
553+
"android-adsjs": [
554+
"apiManipulation",
555+
"webCompat",
556+
"fingerprintingHardware",
557+
"fingerprintingScreenSize",
558+
"fingerprintingTemporaryStorage",
559+
"fingerprintingAudio",
560+
"fingerprintingBattery",
561+
"gpc",
562+
"breakageReporting"
563+
],
533564
windows: [
534565
"cookie",
535566
...baseFeatures,
@@ -1524,6 +1555,246 @@
15241555
}
15251556
};
15261557

1558+
// ../messaging/lib/android-adsjs.js
1559+
var AndroidAdsjsMessagingTransport = class {
1560+
/**
1561+
* @param {AndroidAdsjsMessagingConfig} config
1562+
* @param {MessagingContext} messagingContext
1563+
* @internal
1564+
*/
1565+
constructor(config, messagingContext) {
1566+
this.messagingContext = messagingContext;
1567+
this.config = config;
1568+
this.config.sendInitialPing(messagingContext);
1569+
}
1570+
/**
1571+
* @param {NotificationMessage} msg
1572+
*/
1573+
notify(msg) {
1574+
try {
1575+
this.config.sendMessageThrows?.(msg);
1576+
} catch (e) {
1577+
console.error(".notify failed", e);
1578+
}
1579+
}
1580+
/**
1581+
* @param {RequestMessage} msg
1582+
* @return {Promise<any>}
1583+
*/
1584+
request(msg) {
1585+
return new Promise((resolve, reject) => {
1586+
const unsub = this.config.subscribe(msg.id, handler);
1587+
try {
1588+
this.config.sendMessageThrows?.(msg);
1589+
} catch (e) {
1590+
unsub();
1591+
reject(new Error("request failed to send: " + e.message || "unknown error"));
1592+
}
1593+
function handler(data) {
1594+
if (isResponseFor(msg, data)) {
1595+
if (data.result) {
1596+
resolve(data.result || {});
1597+
return unsub();
1598+
}
1599+
if (data.error) {
1600+
reject(new Error(data.error.message));
1601+
return unsub();
1602+
}
1603+
unsub();
1604+
throw new Error("unreachable: must have `result` or `error` key by this point");
1605+
}
1606+
}
1607+
});
1608+
}
1609+
/**
1610+
* @param {Subscription} msg
1611+
* @param {(value: unknown | undefined) => void} callback
1612+
*/
1613+
subscribe(msg, callback) {
1614+
const unsub = this.config.subscribe(msg.subscriptionName, (data) => {
1615+
if (isSubscriptionEventFor(msg, data)) {
1616+
callback(data.params || {});
1617+
}
1618+
});
1619+
return () => {
1620+
unsub();
1621+
};
1622+
}
1623+
};
1624+
var AndroidAdsjsMessagingConfig = class {
1625+
/**
1626+
* @param {object} params
1627+
* @param {Record<string, any>} params.target
1628+
* @param {boolean} params.debug
1629+
* @param {string} params.objectName - the object name for addWebMessageListener
1630+
*/
1631+
constructor(params) {
1632+
/** @type {{
1633+
* postMessage: (message: string) => void,
1634+
* addEventListener: (type: string, listener: (event: MessageEvent) => void) => void,
1635+
* } | null} */
1636+
__publicField(this, "_capturedHandler");
1637+
this.target = params.target;
1638+
this.debug = params.debug;
1639+
this.objectName = params.objectName;
1640+
this.listeners = new globalThis.Map();
1641+
this._captureGlobalHandler();
1642+
this._setupEventListener();
1643+
}
1644+
/**
1645+
* The transport can call this to transmit a JSON payload along with a secret
1646+
* to the native Android handler via postMessage.
1647+
*
1648+
* Note: This can throw - it's up to the transport to handle the error.
1649+
*
1650+
* @type {(json: object) => void}
1651+
* @throws
1652+
* @internal
1653+
*/
1654+
sendMessageThrows(message) {
1655+
if (!this.objectName) {
1656+
throw new Error("Object name not set for WebMessageListener");
1657+
}
1658+
if (this._capturedHandler && this._capturedHandler.postMessage) {
1659+
this._capturedHandler.postMessage(JSON.stringify(message));
1660+
} else {
1661+
throw new Error("postMessage not available");
1662+
}
1663+
}
1664+
/**
1665+
* A subscription on Android is just a named listener. All messages from
1666+
* android -> are delivered through a single function, and this mapping is used
1667+
* to route the messages to the correct listener.
1668+
*
1669+
* Note: Use this to implement request->response by unsubscribing after the first
1670+
* response.
1671+
*
1672+
* @param {string} id
1673+
* @param {(msg: MessageResponse | SubscriptionEvent) => void} callback
1674+
* @returns {() => void}
1675+
* @internal
1676+
*/
1677+
subscribe(id, callback) {
1678+
this.listeners.set(id, callback);
1679+
return () => {
1680+
this.listeners.delete(id);
1681+
};
1682+
}
1683+
/**
1684+
* Accept incoming messages and try to deliver it to a registered listener.
1685+
*
1686+
* This code is defensive to prevent any single handler from affecting another if
1687+
* it throws (producer interference).
1688+
*
1689+
* @param {MessageResponse | SubscriptionEvent} payload
1690+
* @internal
1691+
*/
1692+
_dispatch(payload) {
1693+
if (!payload) return this._log("no response");
1694+
if ("id" in payload) {
1695+
if (this.listeners.has(payload.id)) {
1696+
this._tryCatch(() => this.listeners.get(payload.id)?.(payload));
1697+
} else {
1698+
this._log("no listeners for ", payload);
1699+
}
1700+
}
1701+
if ("subscriptionName" in payload) {
1702+
if (this.listeners.has(payload.subscriptionName)) {
1703+
this._tryCatch(() => this.listeners.get(payload.subscriptionName)?.(payload));
1704+
} else {
1705+
this._log("no subscription listeners for ", payload);
1706+
}
1707+
}
1708+
}
1709+
/**
1710+
*
1711+
* @param {(...args: any[]) => any} fn
1712+
* @param {string} [context]
1713+
*/
1714+
_tryCatch(fn, context = "none") {
1715+
try {
1716+
return fn();
1717+
} catch (e) {
1718+
if (this.debug) {
1719+
console.error("AndroidAdsjsMessagingConfig error:", context);
1720+
console.error(e);
1721+
}
1722+
}
1723+
}
1724+
/**
1725+
* @param {...any} args
1726+
*/
1727+
_log(...args) {
1728+
if (this.debug) {
1729+
console.log("AndroidAdsjsMessagingConfig", ...args);
1730+
}
1731+
}
1732+
/**
1733+
* Capture the global handler and remove it from the global object.
1734+
*/
1735+
_captureGlobalHandler() {
1736+
const { target, objectName } = this;
1737+
if (Object.prototype.hasOwnProperty.call(target, objectName)) {
1738+
this._capturedHandler = target[objectName];
1739+
delete target[objectName];
1740+
} else {
1741+
this._capturedHandler = null;
1742+
this._log("Android adsjs messaging interface not available", objectName);
1743+
}
1744+
}
1745+
/**
1746+
* Set up event listener for incoming messages from the captured handler.
1747+
*/
1748+
_setupEventListener() {
1749+
if (!this._capturedHandler || !this._capturedHandler.addEventListener) {
1750+
this._log("No event listener support available");
1751+
return;
1752+
}
1753+
this._capturedHandler.addEventListener("message", (event) => {
1754+
try {
1755+
const data = (
1756+
/** @type {MessageEvent} */
1757+
event.data
1758+
);
1759+
if (typeof data === "string") {
1760+
const parsedData = JSON.parse(data);
1761+
this._dispatch(parsedData);
1762+
}
1763+
} catch (e) {
1764+
this._log("Error processing incoming message:", e);
1765+
}
1766+
});
1767+
}
1768+
/**
1769+
* Send an initial ping message to the platform to establish communication.
1770+
* This is a fire-and-forget notification that signals the JavaScript side is ready.
1771+
* Only sends in top context (not in frames) and if the messaging interface is available.
1772+
*
1773+
* @param {MessagingContext} messagingContext
1774+
* @returns {boolean} true if ping was sent, false if in frame or interface not ready
1775+
*/
1776+
sendInitialPing(messagingContext) {
1777+
if (isBeingFramed()) {
1778+
this._log("Skipping initial ping - running in frame context");
1779+
return false;
1780+
}
1781+
try {
1782+
const message = new RequestMessage({
1783+
id: "initialPing",
1784+
context: messagingContext.context,
1785+
featureName: "messaging",
1786+
method: "initialPing"
1787+
});
1788+
this.sendMessageThrows(message);
1789+
this._log("Initial ping sent successfully");
1790+
return true;
1791+
} catch (e) {
1792+
this._log("Failed to send initial ping:", e);
1793+
return false;
1794+
}
1795+
}
1796+
};
1797+
15271798
// ../messaging/index.js
15281799
var MessagingContext = class {
15291800
/**
@@ -1652,6 +1923,9 @@
16521923
if (config instanceof AndroidMessagingConfig) {
16531924
return new AndroidMessagingTransport(config, messagingContext);
16541925
}
1926+
if (config instanceof AndroidAdsjsMessagingConfig) {
1927+
return new AndroidAdsjsMessagingTransport(config, messagingContext);
1928+
}
16551929
if (config instanceof TestTransportConfig) {
16561930
return new TestTransport(config, messagingContext);
16571931
}
@@ -2926,13 +3200,15 @@
29263200
* @property {string[] | string} [domain]
29273201
* @property {object} [urlPattern]
29283202
* @property {object} [minSupportedVersion]
3203+
* @property {object} [maxSupportedVersion]
29293204
* @property {object} [experiment]
29303205
* @property {string} [experiment.experimentName]
29313206
* @property {string} [experiment.cohort]
29323207
* @property {object} [context]
29333208
* @property {boolean} [context.frame] - true if the condition applies to frames
29343209
* @property {boolean} [context.top] - true if the condition applies to the top frame
29353210
* @property {string} [injectName] - the inject name to match against (e.g., "apple-isolated")
3211+
* @property {boolean} [internal] - true if the condition applies to internal builds
29363212
*/
29373213
/**
29383214
* Takes multiple conditional blocks and returns true if any apply.
@@ -2958,7 +3234,9 @@
29583234
urlPattern: this._matchUrlPatternConditional,
29593235
experiment: this._matchExperimentConditional,
29603236
minSupportedVersion: this._matchMinSupportedVersion,
2961-
injectName: this._matchInjectNameConditional
3237+
maxSupportedVersion: this._matchMaxSupportedVersion,
3238+
injectName: this._matchInjectNameConditional,
3239+
internal: this._matchInternalConditional
29623240
};
29633241
for (const key in conditionBlock) {
29643242
if (!conditionChecks[key]) {
@@ -3049,6 +3327,17 @@
30493327
if (!currentInjectName) return false;
30503328
return conditionBlock.injectName === currentInjectName;
30513329
}
3330+
/**
3331+
* Takes a condition block and returns true if the internal state matches the condition.
3332+
* @param {ConditionBlock} conditionBlock
3333+
* @returns {boolean}
3334+
*/
3335+
_matchInternalConditional(conditionBlock) {
3336+
if (conditionBlock.internal === void 0) return false;
3337+
const isInternal = __privateGet(this, _args)?.platform?.internal;
3338+
if (isInternal === void 0) return false;
3339+
return Boolean(conditionBlock.internal) === Boolean(isInternal);
3340+
}
30523341
/**
30533342
* Takes a condition block and returns true if the platform version satisfies the `minSupportedFeature`
30543343
* @param {ConditionBlock} conditionBlock
@@ -3058,6 +3347,15 @@
30583347
if (!conditionBlock.minSupportedVersion) return false;
30593348
return isSupportedVersion(conditionBlock.minSupportedVersion, __privateGet(this, _args)?.platform?.version);
30603349
}
3350+
/**
3351+
* Takes a condition block and returns true if the platform version satisfies the `maxSupportedFeature`
3352+
* @param {ConditionBlock} conditionBlock
3353+
* @returns {boolean}
3354+
*/
3355+
_matchMaxSupportedVersion(conditionBlock) {
3356+
if (!conditionBlock.maxSupportedVersion) return false;
3357+
return isMaxSupportedVersion(conditionBlock.maxSupportedVersion, __privateGet(this, _args)?.platform?.version);
3358+
}
30613359
/**
30623360
* Return the settings object for a feature
30633361
* @param {string} [featureName] - The name of the feature to get the settings for; defaults to the name of the feature

0 commit comments

Comments
 (0)