Skip to content

Commit c8494c4

Browse files
authored
Merge branch 'main' into jd/ext-detector
2 parents 33c679d + 8658d25 commit c8494c4

Some content is hidden

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

48 files changed

+1041
-699
lines changed

.cursorrules

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,79 @@
1-
# Content Scope Scripts - Cursor Rules
2-
3-
## Documentation References
4-
5-
When asked about Content Scope Scripts topics, refer to these documentation files:
6-
7-
- **API Reference**: `injected/docs/api-reference.md`
8-
- **Feature Development**: `injected/docs/features-guide.md`
9-
- **Platform Integration and engine support**: `injected/docs/platform-integration.md`
10-
- **Development Utilities**: `injected/docs/development-utilities.md`
11-
- **Testing**: `injected/docs/testing-guide.md`
12-
- **Favicon**: `injected/docs/favicon.md`
13-
- **Message Bridge**: `injected/docs/message-bridge.md`
14-
- **Test Pages**: `injected/docs/test-pages-guide.md`
15-
- **Documentation Index**: `injected/docs/README.md`
16-
- **High-level Overview**: `injected/README.md`
1+
# Content Scope Scripts (C-S-S)
2+
3+
Shared JavaScript projects powering privacy features and special pages in DuckDuckGo's native apps (macOS, Windows, iOS, Android).
4+
5+
## Workspaces
6+
7+
This is an npm workspace monorepo with four sub-projects:
8+
9+
### `injected/` - Browser Privacy Features
10+
11+
JavaScript features injected into web pages for privacy protections. Features extend `ConfigFeature` and integrate with remote configuration for per-site enable/disable.
12+
13+
**Features** (in `injected/src/features/`):
14+
- `api-manipulation` - API behavior modifications
15+
- `autofill-import` - Credential import support
16+
- `breakage-reporting` - Site breakage reports
17+
- `broker-protection` - Data broker removal automation
18+
- `click-to-load` - Social embed blocking
19+
- `cookie` - Cookie management
20+
- `duck-player` / `duck-player-native` - YouTube privacy player
21+
- `element-hiding` - Hide page elements
22+
- `exception-handler` - Error handling
23+
- `favicon` - Favicon privacy
24+
- `fingerprinting-*` - Audio, battery, canvas, hardware, screen, storage fingerprint protection
25+
- `google-rejected` - Google rejection handling
26+
- `gpc` - Global Privacy Control
27+
- `harmful-apis` - Dangerous API restrictions
28+
- `message-bridge` - Page↔content script messaging
29+
- `navigator-interface` - Navigator API modifications
30+
- `performance-metrics` - Performance tracking
31+
- `referrer` - Referrer protection
32+
- `web-compat` - Site compatibility fixes
33+
- `web-interference-detection` / `web-telemetry` - Monitoring
34+
35+
**Docs:** `injected/docs/README.md` (index to all docs)
36+
37+
### `special-pages/` - Embedded Browser Pages
38+
39+
Preact-based HTML/CSS/JS applications embedded in browsers. Each page lives in `special-pages/pages/<name>/`.
40+
41+
**Pages:**
42+
- `duckplayer` - YouTube privacy player UI
43+
- `errorpage` - Browser error pages
44+
- `example` - Template for new pages
45+
- `history` - Browsing history viewer
46+
- `new-tab` - New Tab Page
47+
- `onboarding` - First-run experience
48+
- `release-notes` - Browser release notes
49+
- `special-error` - SSL/certificate error pages
50+
51+
**Docs:** `special-pages/README.md`, plus `readme.md` in each page directory
52+
53+
### `messaging/` - Web-Native Communication
54+
55+
Abstraction layer for web↔native messaging: `notify` (fire-and-forget), `request` (async response), `subscribe` (push updates).
56+
57+
**Docs:** `messaging/docs/messaging.md`
58+
59+
### `types-generator/` - Schema to TypeScript
60+
61+
Generates TypeScript types from JSON Schema files. Used by other workspaces.
62+
63+
## Commands
64+
65+
Run from root. Use `nvm use` to set the correct Node version.
66+
67+
| Command | Purpose |
68+
|---------|---------|
69+
| `npm run build` | Build all workspaces |
70+
| `npm run test-unit` | Unit tests (all workspaces) |
71+
| `npm run test-int` | Integration tests (Playwright) |
72+
| `npm run lint` | ESLint + TypeScript + Prettier |
73+
| `npm run lint-fix` | Auto-fix lint issues |
74+
| `npm run serve` | Serve injected test pages (port 3220) |
75+
| `npm run serve-special-pages` | Serve special pages (port 3221) |
76+
77+
## Notes
78+
79+
- When running Playwright commands, use `--reporter list` to prevent the Shell tool from hanging

.github/workflows/build.yml

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ name: Release
33
on:
44
workflow_dispatch:
55
inputs:
6-
version:
6+
version_bump:
77
required: true
8-
description: 'Release version'
8+
description: 'Version bump type'
9+
type: choice
10+
options:
11+
- major
12+
- minor
913

1014
jobs:
1115
release_pr:
@@ -39,6 +43,44 @@ jobs:
3943
git checkout -b releases origin/releases
4044
fi
4145
46+
- name: Calculate next version
47+
id: version
48+
run: |
49+
# Get the latest tag (no longer using v prefix, filter out legacy v-prefixed tags)
50+
LATEST_TAG=$(git tag | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)
51+
52+
# Stop build if no version tag is found
53+
if [ -z "$LATEST_TAG" ]; then
54+
echo "Error: No semantic version tags found in repository"
55+
echo "Expected format: X.Y.Z (e.g., 12.5.0)"
56+
exit 1
57+
fi
58+
59+
echo "Latest tag found: $LATEST_TAG"
60+
61+
# Extract version numbers (remove 'v' prefix if present for backwards compatibility)
62+
VERSION_WITHOUT_V=${LATEST_TAG#v}
63+
64+
# Parse major and minor (ignore patch since we don't support it)
65+
MAJOR=$(echo $VERSION_WITHOUT_V | cut -d. -f1)
66+
MINOR=$(echo $VERSION_WITHOUT_V | cut -d. -f2)
67+
68+
# Calculate next version based on input
69+
if [ "${{ github.event.inputs.version_bump }}" = "major" ]; then
70+
NEXT_MAJOR=$((MAJOR + 1))
71+
NEXT_MINOR=0
72+
else
73+
NEXT_MAJOR=$MAJOR
74+
NEXT_MINOR=$((MINOR + 1))
75+
fi
76+
77+
# Create new version (always without v prefix)
78+
NEXT_VERSION="${NEXT_MAJOR}.${NEXT_MINOR}.0"
79+
80+
echo "Next version: $NEXT_VERSION"
81+
echo "RELEASE_VERSION=$NEXT_VERSION" >> $GITHUB_ENV
82+
echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT
83+
4284
- name: Collect commit ranges
4385
run: |
4486
bash ./scripts/changelog.sh > ${{ github.workspace }}/CHANGELOG.txt
@@ -69,7 +111,7 @@ jobs:
69111
- name: Commit build files
70112
uses: stefanzweifel/git-auto-commit-action@v7
71113
with:
72-
commit_message: 'Release build ${{ github.event.inputs.version }} [ci release]'
114+
commit_message: 'Release build ${{ steps.version.outputs.version }} [ci release]'
73115
commit_options: '--allow-empty'
74116
skip_checkout: true
75117
branch: 'releases'
@@ -87,5 +129,5 @@ jobs:
87129
body_path: ${{ github.workspace }}/CHANGELOG.txt
88130
draft: false
89131
prerelease: false
90-
tag_name: ${{ github.event.inputs.version }}
132+
tag_name: ${{ steps.version.outputs.version }}
91133
target_commitish: 'releases'

injected/src/features/breakage-reporting.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ export default class BreakageReporting extends ContentFeature {
1616
referrer,
1717
};
1818

19+
const getOpener = this.getFeatureSettingEnabled('opener', 'enabled');
20+
if (getOpener) {
21+
result.opener = !!window.opener;
22+
}
23+
const getReloaded = this.getFeatureSettingEnabled('reloaded', 'enabled');
24+
if (getReloaded) {
25+
result.pageReloaded =
26+
(window.performance.navigation && window.performance.navigation.type === 1) ||
27+
/** @type {PerformanceNavigationTiming[]} */
28+
(window.performance.getEntriesByType('navigation')).map((nav) => nav.type).includes('reload');
29+
}
30+
1931
// Only run detectors if explicitly configured
2032
// Fetch interferenceTypes from webInterferenceDetection feature settings
2133
const detectorSettings = this.getFeatureSetting('interferenceTypes', 'webInterferenceDetection');

special-pages/.cursorrules

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Special Pages
2+
3+
Preact-based HTML/CSS/JS applications embedded in DuckDuckGo browsers. Each page is an isolated app with privileged native API access.
4+
5+
## Structure
6+
7+
```
8+
pages/<name>/
9+
├── app/ # Preact components (App.jsx, *.module.css)
10+
├── src/index.js # Page class with messaging methods
11+
├── messages/ # JSON schemas → auto-generates types/
12+
├── types/<name>.ts # Generated types (DO NOT EDIT)
13+
├── public/ # Static assets, index.html, locales/
14+
└── integration-tests/
15+
```
16+
17+
**Shared code:** `shared/` contains reusable components (`Button`, `Card`, `Switch`, `Text`), `Environment` class, `TranslationProvider`, and `createSpecialPageMessaging()`.
18+
19+
## Messaging Pattern
20+
21+
Pages communicate with native via `@duckduckgo/messaging`. Define schemas in `messages/`:
22+
- `*.request.json` + `*.response.json` → async request/response
23+
- `*.notify.json` → fire-and-forget notification
24+
- `*.subscribe.json` → push-based subscription
25+
26+
## TypeScript
27+
28+
JSDoc types in JavaScript files. Import types via `@typedef` after imports:
29+
30+
```javascript
31+
/** @typedef {import('./types.js').MyType} MyType */
32+
```
33+
34+
## Styling
35+
36+
- Use CSS Modules (`*.module.css`)
37+
- Global styles: `shared/styles/`
38+
39+
## Testing
40+
41+
Run from `special-pages/` directory:
42+
43+
| Command | Purpose |
44+
|---------|---------|
45+
| `npm run test-unit` | Unit tests |
46+
| `npm run test-int` | Integration tests (all platforms) |
47+
| `npm run test-int -- --project ios` | Single platform |
48+
| `npm run test.screenshots` | Screenshot tests only |
49+
50+
Integration tests use Page Object pattern—see `integration-tests/<name>.js` for helpers.
51+
52+
Top-level commands (`npm run build`, `npm run lint`) also work from this directory.
53+
54+
## Notes
55+
56+
- When running Playwright commands, use `--reporter list` to prevent the Shell tool from hanging
57+
- Types in `types/` are auto-generated from `messages/` schemas—never edit manually

special-pages/pages/errorpage/public/index.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<meta http-equiv="X-UA-Compatible" content="ie=edge">
88
<link rel="stylesheet" href="style.css">
99
</head>
10-
<body>
10+
<body data-theme-variant="$THEME_VARIANT$">
1111
<div class="content-container">
1212
<div class="error-container">
1313
<div class="header-container">
@@ -21,5 +21,16 @@ <h1 class="error-header">$HEADER$</h1>
2121
<img src="img/logo-horizontal.svg" alt="DuckDuckGo" class="watermark">
2222
</div>
2323
</div>
24+
<script>
25+
/**
26+
* Called by native to update the page theme at runtime
27+
* @param {{ themeVariant: string }} payload
28+
*/
29+
window.onChangeTheme = function(payload) {
30+
if (payload && payload.themeVariant) {
31+
document.body.dataset.themeVariant = payload.themeVariant;
32+
}
33+
};
34+
</script>
2435
</body>
2536
</html>

special-pages/pages/errorpage/public/style.css

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,26 @@ body {
6565
.error-description, .error-header {
6666
color: rgb(210, 210, 210);
6767
}
68-
}
68+
}
69+
70+
/* TODO: Use colour variables from design-tokens */
71+
72+
/* Theme variants - light mode */
73+
[data-theme-variant="coolGray"] { background: #d2d5e3; }
74+
[data-theme-variant="slateBlue"] { background: #d2e5f3; }
75+
[data-theme-variant="green"] { background: #e3eee1; }
76+
[data-theme-variant="violet"] { background: #e7e4f5; }
77+
[data-theme-variant="rose"] { background: #f8ebf5; }
78+
[data-theme-variant="orange"] { background: #fcedd8; }
79+
[data-theme-variant="desert"] { background: #eee9e1; }
80+
81+
/* Theme variants - dark mode */
82+
@media (prefers-color-scheme: dark) {
83+
[data-theme-variant="coolGray"] { background: #2b2f45; }
84+
[data-theme-variant="slateBlue"] { background: #1e3347; }
85+
[data-theme-variant="green"] { background: #203b30; }
86+
[data-theme-variant="violet"] { background: #2e2158; }
87+
[data-theme-variant="rose"] { background: #5b194b; }
88+
[data-theme-variant="orange"] { background: #54240c; }
89+
[data-theme-variant="desert"] { background: #3c3833; }
90+
}

special-pages/pages/errorpage/readme.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,34 @@ So far, the following platforms are supported
1010

1111
- macOS
1212

13+
### Theming
14+
15+
The error page supports theming through two mechanisms:
16+
17+
#### Template Variable: `$THEME_VARIANT$`
18+
19+
Native performs string interpolation to replace `$THEME_VARIANT$` in the HTML with a theme variant name.
20+
21+
If string interpolation is not performed (i.e., `$THEME_VARIANT$` is left as-is), the page falls back to the default styling.
22+
23+
**Supported variants:** `default`, `coolGray`, `slateBlue`, `green`, `violet`, `rose`, `orange`, `desert`
24+
25+
#### Callback: `window.onChangeTheme`
26+
27+
Native can call `window.onChangeTheme(payload)` to update the theme at runtime.
28+
29+
**Payload:**
30+
```json
31+
{
32+
"themeVariant": "coolGray"
33+
}
34+
```
35+
36+
**Example native usage:**
37+
```javascript
38+
window.onChangeTheme({ themeVariant: 'coolGray' });
39+
```
40+
1341
---
1442

1543
## Contributing
@@ -26,4 +54,3 @@ Don't edit the generated files directly - any changes you make will not be refle
2654

2755
Instead, make your changes in `src/` and then run `npm run build` from the root folder
2856
- or to build the special pages only (faster), run `npm run postbuild` instead
29-

special-pages/pages/history/public/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
}
1212
}
1313
</style>
14+
<script>
15+
// $LOADING_COLOR$ is replaced by native via string interpolation.
16+
// This script must be in <head> to block rendering and prevent flash of wrong color.
17+
const loadingColor = "$LOADING_COLOR$";
18+
const hasLoadingColor = !!loadingColor && !loadingColor.startsWith("$");
19+
if (hasLoadingColor) {
20+
document.documentElement.style.background = loadingColor;
21+
}
22+
</script>
1423
<title>History</title>
1524
<meta name="robots" content="noindex,nofollow">
1625
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

special-pages/pages/new-tab/app/components/Icons.js

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -602,35 +602,6 @@ export function CloseSmallIcon(props) {
602602
);
603603
}
604604

605-
/**
606-
* @param {import('preact').JSX.SVGAttributes<SVGSVGElement>} props
607-
*/
608-
export function NewBadgeIcon(props) {
609-
return (
610-
<svg xmlns="http://www.w3.org/2000/svg" width="42" height="16" viewBox="0 0 42 16" fill="none" {...props}>
611-
<path
612-
d="M0 3.99792C0 1.78879 1.79086 -0.0020752 4 -0.0020752H38C40.2091 -0.0020752 42 1.78879 42 3.99792V11.9979C42 14.2071 40.2091 15.9979 38 15.9979H4C1.79086 15.9979 0 14.2071 0 11.9979V3.99792Z"
613-
fill="#F9BE1A"
614-
/>
615-
<path
616-
d="M13.0913 9.1073H13.1812V3.94617H14.8032V12.0497H13.3999L9.64893 6.86707H9.55908V12.0497H7.93604V3.94617H9.35107L13.0913 9.1073Z"
617-
fill="black"
618-
fill-opacity="0.96"
619-
/>
620-
<path
621-
d="M22.144 5.3446H18.4722V7.29871H21.936V8.60144H18.4722V10.6512H22.144V12.0497H16.7759V3.94617H22.144V5.3446Z"
622-
fill="black"
623-
fill-opacity="0.96"
624-
/>
625-
<path
626-
d="M26.4663 9.73621H26.5562L28.0337 3.94617H29.4653L30.9702 9.73621H31.0601L32.312 3.94617H34.064L31.9136 12.0497H30.3247L28.7915 6.59167H28.7017L27.1851 12.0497H25.5854L23.4399 3.94617H25.2036L26.4663 9.73621Z"
627-
fill="black"
628-
fill-opacity="0.96"
629-
/>
630-
</svg>
631-
);
632-
}
633-
634605
/**
635606
* @param {import('preact').JSX.SVGAttributes<SVGSVGElement>} props
636607
*/

0 commit comments

Comments
 (0)