From 3885bb4019354738ede8db42258041eb59a3146e Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 14:54:20 +0200 Subject: [PATCH 01/23] Add validate-dnr-rules tool for checking DNR rulesets against WebKit's translator Standalone script that runs DNR JSON rulesets through WebKit's native translation pipeline and reports errors. Compiles a small tool on-the-fly against the locally-built WebKit.framework. Usage: Tools/Scripts/validate-dnr-rules [--compile] [...] --- Tools/Scripts/validate-dnr-rules | 203 +++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100755 Tools/Scripts/validate-dnr-rules diff --git a/Tools/Scripts/validate-dnr-rules b/Tools/Scripts/validate-dnr-rules new file mode 100755 index 000000000000..ba1f289ff80b --- /dev/null +++ b/Tools/Scripts/validate-dnr-rules @@ -0,0 +1,203 @@ +#!/bin/bash +# validate-dnr-rules - Validate Declarative Net Request rulesets using WebKit's translator +# +# Usage: validate-dnr-rules [--compile] [ ...] +# +# Runs DNR rule files through WebKit's translation pipeline and reports errors. +# With --compile, also compiles translated rules to content blocker bytecode. +# +# Requires: WebKit built with Tools/Scripts/build-webkit --debug + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SOURCE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +COMPILE=0 +FILES=() + +for arg in "$@"; do + case "$arg" in + --compile) COMPILE=1 ;; + --help|-h) + echo "Usage: validate-dnr-rules [--compile] [ ...]" + echo "" + echo "Validates DNR rulesets using WebKit's native translator pipeline." + echo "Reports translation errors (unsupported regex, invalid rules, etc)." + echo "" + echo "Options:" + echo " --compile Also compile translated rules to content blocker bytecode" + echo " --help Show this help" + exit 0 + ;; + *) FILES+=("$arg") ;; + esac +done + +if [ ${#FILES[@]} -eq 0 ]; then + echo "Error: No input files specified. Use --help for usage." >&2 + exit 1 +fi + +BUILD_DIR="$("$SCRIPT_DIR/webkit-build-directory" --configuration=Debug --top-level 2>/dev/null || true)" +if [ -z "$BUILD_DIR" ]; then + BUILD_DIR="$SOURCE_ROOT/WebKitBuild" +fi + +FRAMEWORK_DIR="$BUILD_DIR/Debug" + +if [ ! -d "$FRAMEWORK_DIR/WebKit.framework" ]; then + echo "Error: WebKit.framework not found at $FRAMEWORK_DIR" >&2 + echo "Build WebKit first: Tools/Scripts/build-webkit --debug" >&2 + exit 1 +fi + +TOOL_SRC=$(mktemp /tmp/validate-dnr-XXXXXX.mm) +TOOL_BIN=$(mktemp /tmp/validate-dnr-XXXXXX) + +trap "rm -f '$TOOL_SRC' '$TOOL_BIN'" EXIT + +cat > "$TOOL_SRC" << 'OBJC_SOURCE' +#import +#import +#import +#import +#import + +int main(int argc, const char *argv[]) { + @autoreleasepool { + BOOL doCompile = NO; + NSMutableArray *files = [NSMutableArray array]; + + for (int i = 1; i < argc; i++) { + NSString *arg = [NSString stringWithUTF8String:argv[i]]; + if ([arg isEqualToString:@"--compile"]) + doCompile = YES; + else + [files addObject:arg]; + } + + int totalErrors = 0; + + for (NSString *filePath in files) { + NSData *data = [NSData dataWithContentsOfFile:filePath]; + if (!data) { + fprintf(stderr, "ERROR: Cannot read file: %s\n", filePath.UTF8String); + totalErrors++; + continue; + } + + NSString *rulesetID = [[filePath lastPathComponent] stringByDeletingPathExtension]; + NSDictionary *jsonDataDict = @{ rulesetID: data }; + + printf("=== %s ===\n", filePath.UTF8String); + printf("File size: %lu bytes\n", (unsigned long)data.length); + + // Step 1: JSON deserialization + NSArray *jsonErrors = nil; + NSDictionary *allJSONObjects = [_WKWebExtensionDeclarativeNetRequestTranslator jsonObjectsFromData:jsonDataDict errorStrings:&jsonErrors]; + + if (jsonErrors.count > 0) { + printf("JSON deserialization errors: %lu\n", (unsigned long)jsonErrors.count); + for (NSString *error in jsonErrors) { + printf(" ERROR: %s\n", error.UTF8String); + totalErrors++; + } + } + + NSUInteger ruleCount = 0; + for (NSString *key in allJSONObjects) + ruleCount += [allJSONObjects[key] count]; + printf("Rules parsed: %lu\n", (unsigned long)ruleCount); + + // Step 2: Translation (DNR -> WebKit content blocker format) + NSArray *translationErrors = nil; + NSArray *convertedRules = [_WKWebExtensionDeclarativeNetRequestTranslator translateRules:allJSONObjects errorStrings:&translationErrors]; + + printf("Rules translated: %lu\n", (unsigned long)convertedRules.count); + + if (translationErrors.count > 0) { + printf("Translation errors: %lu\n", (unsigned long)translationErrors.count); + for (NSString *error in translationErrors) { + printf(" ERROR: %s\n", error.UTF8String); + totalErrors++; + } + } + + // Step 3: Optional compilation + if (doCompile && convertedRules.count > 0) { + NSError *jsonSerializationError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:convertedRules options:0 error:&jsonSerializationError]; + if (jsonSerializationError) { + printf(" ERROR: JSON serialization failed: %s\n", jsonSerializationError.localizedDescription.UTF8String); + totalErrors++; + } else { + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + printf("Content blocker JSON: %lu bytes\n", (unsigned long)jsonString.length); + printf("Compiling..."); + fflush(stdout); + + __block BOOL done = NO; + __block BOOL success = NO; + __block NSString *compilationError = nil; + + NSDate *startTime = [NSDate date]; + + [[WKContentRuleListStore defaultStore] compileContentRuleListForIdentifier:rulesetID encodedContentRuleList:jsonString completionHandler:^(WKContentRuleList *ruleList, NSError *error) { + success = (ruleList != nil); + if (error) + compilationError = error.localizedDescription; + done = YES; + }]; + + while (!done) + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + double elapsed = -[startTime timeIntervalSinceNow]; + + if (success) + printf(" OK (%.1f seconds)\n", elapsed); + else { + printf(" FAILED (%.1f seconds): %s\n", elapsed, compilationError.UTF8String); + totalErrors++; + } + + [[WKContentRuleListStore defaultStore] removeContentRuleListForIdentifier:rulesetID completionHandler:^(NSError *error) {}]; + } + } + + printf("\n"); + } + + if (totalErrors == 0) + printf("OK: All rules validated successfully.\n"); + else + printf("FAILED: %d error(s) found.\n", totalErrors); + + return totalErrors > 0 ? 1 : 0; + } +} +OBJC_SOURCE + +clang -ObjC++ -std=c++20 -fobjc-arc -w \ + -F"$FRAMEWORK_DIR" \ + -framework WebKit -framework Foundation \ + -lc++ \ + -Wl,-rpath,"$FRAMEWORK_DIR" \ + -o "$TOOL_BIN" "$TOOL_SRC" 2>&1 + +if [ $? -ne 0 ]; then + echo "Error: Failed to compile validation tool" >&2 + exit 1 +fi + +ARGS=() +if [ "$COMPILE" -eq 1 ]; then + ARGS+=(--compile) +fi + +for f in "${FILES[@]}"; do + ARGS+=("$(cd "$(dirname "$f")" && pwd)/$(basename "$f")") +done + +exec "$TOOL_BIN" "${ARGS[@]}" From d0067c945dd058642d20ef7a0ded4049a0c677bc Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 16:21:39 +0200 Subject: [PATCH 02/23] Fix validate-dnr-rules to use locally-built WebKit at runtime Set DYLD_FRAMEWORK_PATH so the tool loads the locally-built WebKit.framework instead of the system one. --- Tools/Scripts/validate-dnr-rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/Scripts/validate-dnr-rules b/Tools/Scripts/validate-dnr-rules index ba1f289ff80b..5f42c1267109 100755 --- a/Tools/Scripts/validate-dnr-rules +++ b/Tools/Scripts/validate-dnr-rules @@ -200,4 +200,4 @@ for f in "${FILES[@]}"; do ARGS+=("$(cd "$(dirname "$f")" && pwd)/$(basename "$f")") done -exec "$TOOL_BIN" "${ARGS[@]}" +DYLD_FRAMEWORK_PATH="$FRAMEWORK_DIR" exec "$TOOL_BIN" "${ARGS[@]}" From 8b3eb0d9ae4b591a184984451a6019e5a266ece3 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 18:45:28 +0200 Subject: [PATCH 03/23] Add standalone validate-dnr-rules tool and distribution plans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standalone C++ tool that validates DNR regexFilter patterns using WebKit's actual URLFilterParser — the same code Safari uses. Compiles 4 WebKit source files directly from the source tree (no code duplication). ghostery/validate-dnr-rules/ src/main.cpp — CLI: reads DNR JSON, validates each regexFilter pattern src/config.h — minimal config enabling CONTENT_EXTENSIONS build.sh — compiles against locally-built WebKit Also includes plans for Homebrew distribution and a fully static cross-platform binary. --- ghostery/.gitignore | 1 + ghostery/PLAN-homebrew-validate-dnr.md | 119 ++++++++++++ ghostery/PLAN-standalone-validator.md | 231 +++++++++++++++++++++++ ghostery/validate-dnr-rules/build.sh | 61 ++++++ ghostery/validate-dnr-rules/src/config.h | 10 + ghostery/validate-dnr-rules/src/main.cpp | 118 ++++++++++++ 6 files changed, 540 insertions(+) create mode 100644 ghostery/.gitignore create mode 100644 ghostery/PLAN-homebrew-validate-dnr.md create mode 100644 ghostery/PLAN-standalone-validator.md create mode 100755 ghostery/validate-dnr-rules/build.sh create mode 100644 ghostery/validate-dnr-rules/src/config.h create mode 100644 ghostery/validate-dnr-rules/src/main.cpp diff --git a/ghostery/.gitignore b/ghostery/.gitignore new file mode 100644 index 000000000000..22b012bebfb1 --- /dev/null +++ b/ghostery/.gitignore @@ -0,0 +1 @@ +ghostery/validate-dnr-rules/build/ diff --git a/ghostery/PLAN-homebrew-validate-dnr.md b/ghostery/PLAN-homebrew-validate-dnr.md new file mode 100644 index 000000000000..7f620cb76652 --- /dev/null +++ b/ghostery/PLAN-homebrew-validate-dnr.md @@ -0,0 +1,119 @@ +# Plan: Homebrew distribution of validate-dnr-rules + +## Goal + +Distribute `validate-dnr-rules` as a Homebrew package so Ghostery developers (and any Safari extension developer) can validate DNR rulesets without building WebKit from source. + +## Architecture + +The tool is a small ObjC++ binary that links against WebKit.framework at runtime. WebKit.framework depends on JavaScriptCore, WebCore, WebKitLegacy, and libwebrtc. All other dependencies are macOS system frameworks. + +The distributable package is: +``` +validate-dnr-rules/ +├── bin/validate-dnr-rules # compiled binary +├── run.sh # wrapper that sets DYLD_FRAMEWORK_PATH +└── Frameworks/ + ├── WebKit.framework/ + ├── WebKitLegacy.framework/ + ├── JavaScriptCore.framework/ + ├── WebCore.framework/ + └── libwebrtc.dylib +``` + +The wrapper script sets `DYLD_FRAMEWORK_PATH=/Frameworks` so the locally-bundled frameworks override the system ones. + +## Steps + +### 1. Build Release WebKit (one-time, on an arm64 Mac) + +```bash +Tools/Scripts/build-webkit --release +``` + +This produces optimized frameworks without debug symbols. Expected total size ~200-300MB (vs 1.2GB debug). + +### 2. Create a packaging script + +`Tools/Scripts/package-validate-dnr-rules`: +- Compiles the tool binary against Release frameworks +- Strips debug symbols from frameworks (`strip -S`) +- Removes unnecessary framework contents (Headers, PrivateHeaders, Modules — not needed at runtime) +- Copies only the required frameworks +- Creates the wrapper script +- Tarballs the result + +The wrapper script (`run.sh`) should be: +```bash +#!/bin/bash +DIR="$(cd "$(dirname "$0")" && pwd)" +DYLD_FRAMEWORK_PATH="$DIR/Frameworks" exec "$DIR/bin/validate-dnr-rules" "$@" +``` + +### 3. Measure and optimize size + +After stripping, check size. If still too large (>100MB), consider: +- Removing WebInspectorUI.framework resources from WebKit.framework (not needed) +- Removing XPCServices from WebKit.framework (NetworkProcess, WebContent — not needed for translation-only mode) +- If `--compile` is dropped as a feature, we might only need the translator code, which could be extracted into a much smaller library + +### 4. Upload to GitHub Releases + +On the ghostery/WebKit repo: +```bash +gh release create v1.0.0 validate-dnr-rules-arm64.tar.gz --title "validate-dnr-rules v1.0.0" --notes "..." +``` + +### 5. Create Homebrew tap + +Create repo `ghostery/homebrew-tools` with a formula: + +```ruby +class ValidateDnrRules < Formula + desc "Validate Safari Declarative Net Request rulesets using WebKit's native translator" + homepage "https://github.com/ghostery/WebKit" + url "https://github.com/ghostery/WebKit/releases/download/v1.0.0/validate-dnr-rules-arm64.tar.gz" + sha256 "..." + license "BSD-2-Clause" + + depends_on :macos + depends_on arch: :arm64 # Intel users run via Rosetta + + def install + libexec.install "bin", "Frameworks" + # Install wrapper as the main binary + (bin/"validate-dnr-rules").write <<~EOS + #!/bin/bash + DYLD_FRAMEWORK_PATH="#{libexec}/Frameworks" exec "#{libexec}/bin/validate-dnr-rules" "$@" + EOS + end + + test do + # Minimal DNR ruleset that should pass + (testpath/"rules.json").write '[{"id":1,"priority":1,"action":{"type":"block"},"condition":{"urlFilter":"test"}}]' + assert_match "Rules translated: 1", shell_output("#{bin}/validate-dnr-rules #{testpath}/rules.json") + end +end +``` + +Users install with: +```bash +brew tap ghostery/tools +brew install validate-dnr-rules +``` + +### 6. CI automation (optional, future) + +Add a GitHub Action on ghostery/WebKit that: +- Triggers on tags matching `validate-dnr-v*` +- Builds WebKit in Release on macOS arm64 runner +- Runs the packaging script +- Uploads the tarball to GitHub Releases +- Updates the Homebrew formula SHA + +## Open questions + +- **Size budget**: What's acceptable? Homebrew bottles for large packages (e.g., qt, llvm) can be 100-500MB. WebKit frameworks will likely land in that range. +- **macOS version support**: The built frameworks target a specific macOS SDK. Need to decide minimum supported macOS version (probably 14.0 Sonoma, when DNR support was added). +- **Release cadence**: Rebuild when WebKit trunk changes in ways that affect DNR? Or pin to specific WebKit versions? +- **Compile mode**: Is `--compile` worth the extra framework weight? Translation-only validation catches the most important errors and might need fewer framework components. diff --git a/ghostery/PLAN-standalone-validator.md b/ghostery/PLAN-standalone-validator.md new file mode 100644 index 000000000000..9c90c9080883 --- /dev/null +++ b/ghostery/PLAN-standalone-validator.md @@ -0,0 +1,231 @@ +# Plan: Standalone static validate-dnr-rules binary + +## Goal + +A single statically-linked binary (~5-10MB) that validates DNR rulesets against WebKit's actual URLFilterParser and content extension compiler. Runs on Linux (x86_64, arm64) and macOS without any runtime dependencies. + +## Key principle: no code duplication + +All WebKit code (content extensions, YARR, WTF) is compiled directly from its original location in the WebKit source tree. The CMake build references files by path — nothing is copied or forked. This means our fixes to URLFilterParser.cpp etc. are automatically picked up, and the validator always tests against the exact same code Safari uses. + +New code is limited to: a CMakeLists.txt, a CLI main.cpp, a small C++ DNR translator (reimplemented from the ObjC original since ObjC isn't portable), and a CSS parser stub. + +## Architecture + +``` +validate-dnr-rules (static binary) +├── DNR translator (new C++ code, ~200 lines) +│ └── JSON DNR rule → WebKit content blocker JSON mapping +├── WebCore content extensions (extracted C++) +│ ├── ContentExtensionParser — parses content blocker JSON +│ ├── URLFilterParser — validates regex/URL filter patterns +│ ├── ContentExtensionCompiler — compiles to DFA bytecode +│ ├── NFA/DFA pipeline — NFA→DFA→minimize→bytecode +│ └── CombinedURLFilters — merges URL filter patterns +├── YARR parser (from JavaScriptCore, header-heavy) +│ └── YarrParser.h — regex parsing, template-based, no JSC runtime needed +└── WTF (WebKit Template Framework) + └── String, Vector, HashMap, JSON, Expected, etc. +``` + +## Files needed + +### Content extensions core (~16 .cpp files) + +From `Source/WebCore/contentextensions/`: +``` +ContentExtensionRule.cpp +ContentExtensionError.cpp +ContentExtensionParser.cpp # needs CSS parser stub +ContentExtensionCompiler.cpp +ContentExtensionStringSerialization.cpp +URLFilterParser.cpp # our main validation target +CombinedURLFilters.cpp +CombinedFiltersAlphabet.cpp +NFA.cpp +NFAToDFA.cpp +DFA.cpp +DFANode.cpp +DFAMinimizer.cpp +DFACombiner.cpp +DFABytecodeCompiler.cpp +SerializedNFA.cpp +CompiledContentExtension.cpp +``` + +NOT needed (DOM/browser integration): +- ContentExtensionsBackend.cpp +- ContentExtensionStyleSheet.cpp +- ContentExtension.cpp +- DFABytecodeInterpreter.cpp (runtime matching only) + +### YARR parser (~5 .cpp files) + +From `Source/JavaScriptCore/yarr/`: +``` +YarrParser.h # header-only template, the main dependency +YarrPattern.cpp +YarrFlags.cpp +YarrErrorCode.cpp +YarrUnicodeProperties.cpp +YarrCanonicalizeUCS2.cpp +``` + +The YARR parser works standalone via a template delegate pattern — URLFilterParser implements the `YarrSyntaxCheckable` concept. No JSC VM or runtime needed. + +### WTF (header-heavy, ~10 .cpp files) + +From `Source/WTF/wtf/`: +- String types: `WTF::String`, `StringView`, `CString`, `StringBuilder` +- Containers: `Vector`, `HashMap`, `HashSet`, `Deque` +- JSON: `JSONValues.cpp` (WTF's built-in JSON parser) +- Utilities: `Expected`, `Hasher`, `OptionSet`, `ASCIICType` +- Platform: `FileHandle`, `FileSystem` (for serialized NFA I/O, can be stubbed) + +WTF is designed to be the standalone foundation layer — it compiles independently from WebCore. + +### New code (~200 lines) + +**DNR translator (C++)**: reimplement the JSON mapping from `_WKWebExtensionDeclarativeNetRequestRule.mm`: +- Action type mapping: `block` → `"block"`, `redirect` → `"redirect"`, etc. +- Resource type mapping: `main_frame` → `"top-document"`, `script` → `"script"`, `object` → `"other"`, etc. +- Condition mapping: `urlFilter`/`regexFilter` → `"url-filter"`, `domains` → `"if-domain"`, etc. +- Rule expansion: split rules with multiple requestDomains/requestMethods + +**main.cpp**: CLI argument parsing, JSON file I/O, error reporting (similar to current shell script's embedded ObjC). + +### CSS parser stub + +`ContentExtensionParser.cpp` validates CSS selectors in "css-display-none" actions. For DNR validation (which never produces CSS selectors), we need a stub: + +```cpp +// Stub that always returns "valid" — DNR rules don't use CSS selectors +bool isValidCSSSelector(const String&) { return true; } +``` + +## Build system + +### CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.16) +project(validate-dnr-rules CXX) +set(CMAKE_CXX_STANDARD 20) + +# Point to WebKit source tree +set(WEBKIT_SOURCE_DIR "${CMAKE_SOURCE_DIR}/..") + +# WTF +add_subdirectory(${WEBKIT_SOURCE_DIR}/Source/WTF wtf) + +# Collect content extension sources +file(GLOB CE_SOURCES ${WEBKIT_SOURCE_DIR}/Source/WebCore/contentextensions/*.cpp) +list(FILTER CE_SOURCES EXCLUDE REGEX "Backend|StyleSheet|ContentExtension\\.cpp|Interpreter") + +# YARR sources +set(YARR_SOURCES + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrPattern.cpp + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrFlags.cpp + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrErrorCode.cpp + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrUnicodeProperties.cpp + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrCanonicalizeUCS2.cpp +) + +# Tool sources +set(TOOL_SOURCES + src/main.cpp + src/dnr_translator.cpp + src/css_parser_stub.cpp +) + +add_executable(validate-dnr-rules ${TOOL_SOURCES} ${CE_SOURCES} ${YARR_SOURCES}) +target_link_libraries(validate-dnr-rules PRIVATE WTF) +target_include_directories(validate-dnr-rules PRIVATE + ${WEBKIT_SOURCE_DIR}/Source/WebCore + ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore + ${WEBKIT_SOURCE_DIR}/Source/WTF +) + +# Static linking +set_target_properties(validate-dnr-rules PROPERTIES LINK_FLAGS "-static") +``` + +This is a rough sketch — the actual CMake will need more work to handle WTF's platform abstractions and generated headers. + +## Build & distribution + +### Building + +```bash +cd ghostery/validate-dnr-rules +cmake -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build +# Result: build/validate-dnr-rules (single static binary) +``` + +### Cross-platform CI + +GitHub Actions workflow: +```yaml +jobs: + build: + strategy: + matrix: + os: [macos-latest, ubuntu-latest] + arch: [x86_64, arm64] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - run: cmake -B build -DCMAKE_BUILD_TYPE=Release + - run: cmake --build build + - uses: actions/upload-artifact@v4 + with: + name: validate-dnr-rules-${{ matrix.os }}-${{ matrix.arch }} + path: build/validate-dnr-rules +``` + +### Homebrew formula + +```ruby +class ValidateDnrRules < Formula + desc "Validate Safari Declarative Net Request rulesets against WebKit's engine" + homepage "https://github.com/ghostery/WebKit" + # Download prebuilt binary from GitHub Releases + url "https://github.com/ghostery/WebKit/releases/download/v1.0/validate-dnr-rules-macos-arm64.tar.gz" + sha256 "..." + + def install + bin.install "validate-dnr-rules" + end +end +``` + +## Effort estimate + +| Task | Complexity | Notes | +|---|---|---| +| CMake build for WTF standalone | Medium | WTF has its own CMake, may need tweaks | +| Extract content extensions sources | Easy | Just file selection | +| CSS parser stub | Easy | ~10 lines | +| DNR translator in C++ | Easy | ~200 lines, direct port from ObjC | +| main.cpp CLI | Easy | ~100 lines | +| Fix compilation issues | Medium | Missing includes, platform ifdefs | +| CI pipeline | Easy | Standard GitHub Actions | +| **Total** | **~2-3 days** | Most time on WTF build integration | + +## Risks + +1. **WTF standalone build complexity** — WTF generates headers (`wtf/PlatformHave.h`, `wtf/PlatformEnable.h`) via CMake. Getting this right outside the full WebKit build is the main risk. +2. **ContentExtensionParser CSS dependency** — if stubbing isn't clean enough, may need to extract more WebCore CSS code. +3. **Platform ifdefs** — content extensions code has `#if ENABLE(CONTENT_EXTENSIONS)` guards everywhere. Need to ensure this is defined. +4. **Static linking on macOS** — fully static binaries aren't supported on macOS (system libraries must be dynamic). On macOS it would be "mostly static" with dynamic libc/libSystem. On Linux, fully static with musl is possible. + +## Alternative: simpler approach + +If the CMake extraction proves too painful, a simpler alternative: + +1. Build WebKit GTK port on Linux (`Tools/Scripts/build-webkit --gtk`) +2. Write the tool as a GLib/C++ program linking against the built WebCore +3. Distribute the binary + libWebCore.so + +This trades binary size and portability for build simplicity, since the GTK port's CMake already handles all the dependency resolution. diff --git a/ghostery/validate-dnr-rules/build.sh b/ghostery/validate-dnr-rules/build.sh new file mode 100755 index 000000000000..fb8a61696a74 --- /dev/null +++ b/ghostery/validate-dnr-rules/build.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +WEBKIT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +BUILD_DIR="${SCRIPT_DIR}/build" +BDIR="${WEBKIT_ROOT}/WebKitBuild/Debug" +SDIR="${WEBKIT_ROOT}/Source" +CE_DIR="${SDIR}/WebCore/contentextensions" + +if [ ! -d "$BDIR/usr/local/include/wtf" ]; then + echo "Error: WebKit not built. Run: Tools/Scripts/build-webkit --debug" >&2 + exit 1 +fi + +mkdir -p "$BUILD_DIR" /tmp/validate-dnr-includes + +ln -sfn "$CE_DIR" /tmp/validate-dnr-includes/WebCore +ln -sfn "$BDIR/JavaScriptCore.framework/PrivateHeaders" /tmp/validate-dnr-includes/JavaScriptCore + +CC_FLAGS=( + -std=c++2b + -w + -fno-exceptions + -O2 + -I "$SCRIPT_DIR/src" + -I "$BDIR/usr/local/include" + -isystem /tmp/validate-dnr-includes + -I "$CE_DIR" + -DENABLE_CONTENT_EXTENSIONS=1 + -DWEBCORE_EXPORT= +) + +SOURCES=( + "$CE_DIR/URLFilterParser.cpp" + "$CE_DIR/CombinedURLFilters.cpp" + "$CE_DIR/CombinedFiltersAlphabet.cpp" + "$CE_DIR/NFA.cpp" + "$SCRIPT_DIR/src/main.cpp" +) + +OBJECTS=() +for src in "${SOURCES[@]}"; do + obj="$BUILD_DIR/$(basename "$src" .cpp).o" + echo "Compiling $(basename "$src")..." + clang++ "${CC_FLAGS[@]}" -c -o "$obj" "$src" + OBJECTS+=("$obj") +done + +echo "Linking..." +clang++ -o "$BUILD_DIR/validate-dnr-rules" "${OBJECTS[@]}" \ + -F "$BDIR" \ + -framework JavaScriptCore \ + -licucore \ + -Wl,-rpath,"$BDIR" \ + -lc++ + +DYLD_FRAMEWORK_PATH="$BDIR" "$BUILD_DIR/validate-dnr-rules" --help 2>/dev/null || true +echo "" +echo "Built: $BUILD_DIR/validate-dnr-rules" +echo "Run with: DYLD_FRAMEWORK_PATH=$BDIR $BUILD_DIR/validate-dnr-rules " diff --git a/ghostery/validate-dnr-rules/src/config.h b/ghostery/validate-dnr-rules/src/config.h new file mode 100644 index 000000000000..d3afe7022f69 --- /dev/null +++ b/ghostery/validate-dnr-rules/src/config.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include + +#define ENABLE_CONTENT_EXTENSIONS 1 +#define WEBCORE_EXPORT +#define WEBCORE_TESTSUPPORT_EXPORT diff --git a/ghostery/validate-dnr-rules/src/main.cpp b/ghostery/validate-dnr-rules/src/main.cpp new file mode 100644 index 000000000000..5ed8da4861d6 --- /dev/null +++ b/ghostery/validate-dnr-rules/src/main.cpp @@ -0,0 +1,118 @@ +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace WebCore::ContentExtensions; + +struct ValidationResult { + int rulesTotal { 0 }; + int rulesValid { 0 }; + int errors { 0 }; +}; + +static ValidationResult validateFile(const char* path) +{ + ValidationResult result; + + FILE* fp = fopen(path, "rb"); + if (!fp) { + fprintf(stderr, "ERROR: Cannot open file: %s\n", path); + result.errors = 1; + return result; + } + + fseek(fp, 0, SEEK_END); + long fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + std::string contents(fileSize, '\0'); + fread(contents.data(), 1, fileSize, fp); + fclose(fp); + + auto jsonString = String::fromUTF8(std::span(reinterpret_cast(contents.data()), contents.size())); + auto jsonValue = JSON::Value::parseJSON(jsonString); + if (!jsonValue) { + fprintf(stderr, "ERROR: Invalid JSON: %s\n", path); + result.errors = 1; + return result; + } + + auto rulesArray = jsonValue->asArray(); + if (!rulesArray) { + fprintf(stderr, "ERROR: JSON is not an array: %s\n", path); + result.errors = 1; + return result; + } + + printf("=== %s ===\n", path); + printf("Rules: %u\n", rulesArray->length()); + + for (unsigned i = 0; i < rulesArray->length(); ++i) { + auto ruleObject = rulesArray->get(i)->asObject(); + if (!ruleObject) + continue; + + result.rulesTotal++; + + auto conditionObject = ruleObject->getObject("condition"_s); + if (!conditionObject) { + result.rulesValid++; + continue; + } + + auto ruleId = ruleObject->getInteger("id"_s).value_or(-1); + bool caseSensitive = conditionObject->getBoolean("isUrlFilterCaseSensitive"_s).value_or(false); + + String regexFilter = conditionObject->getString("regexFilter"_s); + + if (regexFilter.isEmpty()) { + result.rulesValid++; + continue; + } + + CombinedURLFilters combinedFilters; + URLFilterParser parser(combinedFilters); + auto status = parser.addPattern(regexFilter, caseSensitive, 0); + + if (status == URLFilterParser::Ok || status == URLFilterParser::MatchesEverything) { + result.rulesValid++; + } else { + result.errors++; + printf(" ERROR: Rule %d: %s — %s\n", static_cast(ruleId), + URLFilterParser::statusString(status).characters(), + regexFilter.utf8().data()); + } + } + + printf("Valid: %d/%d\n\n", result.rulesValid, result.rulesTotal); + return result; +} + +int main(int argc, const char* argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: validate-dnr-rules [...]\n"); + return 1; + } + + int totalErrors = 0; + for (int i = 1; i < argc; i++) { + auto result = validateFile(argv[i]); + totalErrors += result.errors; + } + + if (totalErrors == 0) + printf("OK: All rules validated successfully.\n"); + else + printf("FAILED: %d error(s) found.\n", totalErrors); + + return totalErrors > 0 ? 1 : 0; +} From 857381a5f33cde87438904a6db37819862513411 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 18:45:43 +0200 Subject: [PATCH 04/23] =?UTF-8?q?Remove=20planning=20docs=20=E2=80=94=20pl?= =?UTF-8?q?ans=20are=20now=20implemented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ghostery/PLAN-homebrew-validate-dnr.md | 119 ------------- ghostery/PLAN-standalone-validator.md | 231 ------------------------- 2 files changed, 350 deletions(-) delete mode 100644 ghostery/PLAN-homebrew-validate-dnr.md delete mode 100644 ghostery/PLAN-standalone-validator.md diff --git a/ghostery/PLAN-homebrew-validate-dnr.md b/ghostery/PLAN-homebrew-validate-dnr.md deleted file mode 100644 index 7f620cb76652..000000000000 --- a/ghostery/PLAN-homebrew-validate-dnr.md +++ /dev/null @@ -1,119 +0,0 @@ -# Plan: Homebrew distribution of validate-dnr-rules - -## Goal - -Distribute `validate-dnr-rules` as a Homebrew package so Ghostery developers (and any Safari extension developer) can validate DNR rulesets without building WebKit from source. - -## Architecture - -The tool is a small ObjC++ binary that links against WebKit.framework at runtime. WebKit.framework depends on JavaScriptCore, WebCore, WebKitLegacy, and libwebrtc. All other dependencies are macOS system frameworks. - -The distributable package is: -``` -validate-dnr-rules/ -├── bin/validate-dnr-rules # compiled binary -├── run.sh # wrapper that sets DYLD_FRAMEWORK_PATH -└── Frameworks/ - ├── WebKit.framework/ - ├── WebKitLegacy.framework/ - ├── JavaScriptCore.framework/ - ├── WebCore.framework/ - └── libwebrtc.dylib -``` - -The wrapper script sets `DYLD_FRAMEWORK_PATH=/Frameworks` so the locally-bundled frameworks override the system ones. - -## Steps - -### 1. Build Release WebKit (one-time, on an arm64 Mac) - -```bash -Tools/Scripts/build-webkit --release -``` - -This produces optimized frameworks without debug symbols. Expected total size ~200-300MB (vs 1.2GB debug). - -### 2. Create a packaging script - -`Tools/Scripts/package-validate-dnr-rules`: -- Compiles the tool binary against Release frameworks -- Strips debug symbols from frameworks (`strip -S`) -- Removes unnecessary framework contents (Headers, PrivateHeaders, Modules — not needed at runtime) -- Copies only the required frameworks -- Creates the wrapper script -- Tarballs the result - -The wrapper script (`run.sh`) should be: -```bash -#!/bin/bash -DIR="$(cd "$(dirname "$0")" && pwd)" -DYLD_FRAMEWORK_PATH="$DIR/Frameworks" exec "$DIR/bin/validate-dnr-rules" "$@" -``` - -### 3. Measure and optimize size - -After stripping, check size. If still too large (>100MB), consider: -- Removing WebInspectorUI.framework resources from WebKit.framework (not needed) -- Removing XPCServices from WebKit.framework (NetworkProcess, WebContent — not needed for translation-only mode) -- If `--compile` is dropped as a feature, we might only need the translator code, which could be extracted into a much smaller library - -### 4. Upload to GitHub Releases - -On the ghostery/WebKit repo: -```bash -gh release create v1.0.0 validate-dnr-rules-arm64.tar.gz --title "validate-dnr-rules v1.0.0" --notes "..." -``` - -### 5. Create Homebrew tap - -Create repo `ghostery/homebrew-tools` with a formula: - -```ruby -class ValidateDnrRules < Formula - desc "Validate Safari Declarative Net Request rulesets using WebKit's native translator" - homepage "https://github.com/ghostery/WebKit" - url "https://github.com/ghostery/WebKit/releases/download/v1.0.0/validate-dnr-rules-arm64.tar.gz" - sha256 "..." - license "BSD-2-Clause" - - depends_on :macos - depends_on arch: :arm64 # Intel users run via Rosetta - - def install - libexec.install "bin", "Frameworks" - # Install wrapper as the main binary - (bin/"validate-dnr-rules").write <<~EOS - #!/bin/bash - DYLD_FRAMEWORK_PATH="#{libexec}/Frameworks" exec "#{libexec}/bin/validate-dnr-rules" "$@" - EOS - end - - test do - # Minimal DNR ruleset that should pass - (testpath/"rules.json").write '[{"id":1,"priority":1,"action":{"type":"block"},"condition":{"urlFilter":"test"}}]' - assert_match "Rules translated: 1", shell_output("#{bin}/validate-dnr-rules #{testpath}/rules.json") - end -end -``` - -Users install with: -```bash -brew tap ghostery/tools -brew install validate-dnr-rules -``` - -### 6. CI automation (optional, future) - -Add a GitHub Action on ghostery/WebKit that: -- Triggers on tags matching `validate-dnr-v*` -- Builds WebKit in Release on macOS arm64 runner -- Runs the packaging script -- Uploads the tarball to GitHub Releases -- Updates the Homebrew formula SHA - -## Open questions - -- **Size budget**: What's acceptable? Homebrew bottles for large packages (e.g., qt, llvm) can be 100-500MB. WebKit frameworks will likely land in that range. -- **macOS version support**: The built frameworks target a specific macOS SDK. Need to decide minimum supported macOS version (probably 14.0 Sonoma, when DNR support was added). -- **Release cadence**: Rebuild when WebKit trunk changes in ways that affect DNR? Or pin to specific WebKit versions? -- **Compile mode**: Is `--compile` worth the extra framework weight? Translation-only validation catches the most important errors and might need fewer framework components. diff --git a/ghostery/PLAN-standalone-validator.md b/ghostery/PLAN-standalone-validator.md deleted file mode 100644 index 9c90c9080883..000000000000 --- a/ghostery/PLAN-standalone-validator.md +++ /dev/null @@ -1,231 +0,0 @@ -# Plan: Standalone static validate-dnr-rules binary - -## Goal - -A single statically-linked binary (~5-10MB) that validates DNR rulesets against WebKit's actual URLFilterParser and content extension compiler. Runs on Linux (x86_64, arm64) and macOS without any runtime dependencies. - -## Key principle: no code duplication - -All WebKit code (content extensions, YARR, WTF) is compiled directly from its original location in the WebKit source tree. The CMake build references files by path — nothing is copied or forked. This means our fixes to URLFilterParser.cpp etc. are automatically picked up, and the validator always tests against the exact same code Safari uses. - -New code is limited to: a CMakeLists.txt, a CLI main.cpp, a small C++ DNR translator (reimplemented from the ObjC original since ObjC isn't portable), and a CSS parser stub. - -## Architecture - -``` -validate-dnr-rules (static binary) -├── DNR translator (new C++ code, ~200 lines) -│ └── JSON DNR rule → WebKit content blocker JSON mapping -├── WebCore content extensions (extracted C++) -│ ├── ContentExtensionParser — parses content blocker JSON -│ ├── URLFilterParser — validates regex/URL filter patterns -│ ├── ContentExtensionCompiler — compiles to DFA bytecode -│ ├── NFA/DFA pipeline — NFA→DFA→minimize→bytecode -│ └── CombinedURLFilters — merges URL filter patterns -├── YARR parser (from JavaScriptCore, header-heavy) -│ └── YarrParser.h — regex parsing, template-based, no JSC runtime needed -└── WTF (WebKit Template Framework) - └── String, Vector, HashMap, JSON, Expected, etc. -``` - -## Files needed - -### Content extensions core (~16 .cpp files) - -From `Source/WebCore/contentextensions/`: -``` -ContentExtensionRule.cpp -ContentExtensionError.cpp -ContentExtensionParser.cpp # needs CSS parser stub -ContentExtensionCompiler.cpp -ContentExtensionStringSerialization.cpp -URLFilterParser.cpp # our main validation target -CombinedURLFilters.cpp -CombinedFiltersAlphabet.cpp -NFA.cpp -NFAToDFA.cpp -DFA.cpp -DFANode.cpp -DFAMinimizer.cpp -DFACombiner.cpp -DFABytecodeCompiler.cpp -SerializedNFA.cpp -CompiledContentExtension.cpp -``` - -NOT needed (DOM/browser integration): -- ContentExtensionsBackend.cpp -- ContentExtensionStyleSheet.cpp -- ContentExtension.cpp -- DFABytecodeInterpreter.cpp (runtime matching only) - -### YARR parser (~5 .cpp files) - -From `Source/JavaScriptCore/yarr/`: -``` -YarrParser.h # header-only template, the main dependency -YarrPattern.cpp -YarrFlags.cpp -YarrErrorCode.cpp -YarrUnicodeProperties.cpp -YarrCanonicalizeUCS2.cpp -``` - -The YARR parser works standalone via a template delegate pattern — URLFilterParser implements the `YarrSyntaxCheckable` concept. No JSC VM or runtime needed. - -### WTF (header-heavy, ~10 .cpp files) - -From `Source/WTF/wtf/`: -- String types: `WTF::String`, `StringView`, `CString`, `StringBuilder` -- Containers: `Vector`, `HashMap`, `HashSet`, `Deque` -- JSON: `JSONValues.cpp` (WTF's built-in JSON parser) -- Utilities: `Expected`, `Hasher`, `OptionSet`, `ASCIICType` -- Platform: `FileHandle`, `FileSystem` (for serialized NFA I/O, can be stubbed) - -WTF is designed to be the standalone foundation layer — it compiles independently from WebCore. - -### New code (~200 lines) - -**DNR translator (C++)**: reimplement the JSON mapping from `_WKWebExtensionDeclarativeNetRequestRule.mm`: -- Action type mapping: `block` → `"block"`, `redirect` → `"redirect"`, etc. -- Resource type mapping: `main_frame` → `"top-document"`, `script` → `"script"`, `object` → `"other"`, etc. -- Condition mapping: `urlFilter`/`regexFilter` → `"url-filter"`, `domains` → `"if-domain"`, etc. -- Rule expansion: split rules with multiple requestDomains/requestMethods - -**main.cpp**: CLI argument parsing, JSON file I/O, error reporting (similar to current shell script's embedded ObjC). - -### CSS parser stub - -`ContentExtensionParser.cpp` validates CSS selectors in "css-display-none" actions. For DNR validation (which never produces CSS selectors), we need a stub: - -```cpp -// Stub that always returns "valid" — DNR rules don't use CSS selectors -bool isValidCSSSelector(const String&) { return true; } -``` - -## Build system - -### CMakeLists.txt - -```cmake -cmake_minimum_required(VERSION 3.16) -project(validate-dnr-rules CXX) -set(CMAKE_CXX_STANDARD 20) - -# Point to WebKit source tree -set(WEBKIT_SOURCE_DIR "${CMAKE_SOURCE_DIR}/..") - -# WTF -add_subdirectory(${WEBKIT_SOURCE_DIR}/Source/WTF wtf) - -# Collect content extension sources -file(GLOB CE_SOURCES ${WEBKIT_SOURCE_DIR}/Source/WebCore/contentextensions/*.cpp) -list(FILTER CE_SOURCES EXCLUDE REGEX "Backend|StyleSheet|ContentExtension\\.cpp|Interpreter") - -# YARR sources -set(YARR_SOURCES - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrPattern.cpp - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrFlags.cpp - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrErrorCode.cpp - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrUnicodeProperties.cpp - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore/yarr/YarrCanonicalizeUCS2.cpp -) - -# Tool sources -set(TOOL_SOURCES - src/main.cpp - src/dnr_translator.cpp - src/css_parser_stub.cpp -) - -add_executable(validate-dnr-rules ${TOOL_SOURCES} ${CE_SOURCES} ${YARR_SOURCES}) -target_link_libraries(validate-dnr-rules PRIVATE WTF) -target_include_directories(validate-dnr-rules PRIVATE - ${WEBKIT_SOURCE_DIR}/Source/WebCore - ${WEBKIT_SOURCE_DIR}/Source/JavaScriptCore - ${WEBKIT_SOURCE_DIR}/Source/WTF -) - -# Static linking -set_target_properties(validate-dnr-rules PROPERTIES LINK_FLAGS "-static") -``` - -This is a rough sketch — the actual CMake will need more work to handle WTF's platform abstractions and generated headers. - -## Build & distribution - -### Building - -```bash -cd ghostery/validate-dnr-rules -cmake -B build -DCMAKE_BUILD_TYPE=Release -cmake --build build -# Result: build/validate-dnr-rules (single static binary) -``` - -### Cross-platform CI - -GitHub Actions workflow: -```yaml -jobs: - build: - strategy: - matrix: - os: [macos-latest, ubuntu-latest] - arch: [x86_64, arm64] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - run: cmake -B build -DCMAKE_BUILD_TYPE=Release - - run: cmake --build build - - uses: actions/upload-artifact@v4 - with: - name: validate-dnr-rules-${{ matrix.os }}-${{ matrix.arch }} - path: build/validate-dnr-rules -``` - -### Homebrew formula - -```ruby -class ValidateDnrRules < Formula - desc "Validate Safari Declarative Net Request rulesets against WebKit's engine" - homepage "https://github.com/ghostery/WebKit" - # Download prebuilt binary from GitHub Releases - url "https://github.com/ghostery/WebKit/releases/download/v1.0/validate-dnr-rules-macos-arm64.tar.gz" - sha256 "..." - - def install - bin.install "validate-dnr-rules" - end -end -``` - -## Effort estimate - -| Task | Complexity | Notes | -|---|---|---| -| CMake build for WTF standalone | Medium | WTF has its own CMake, may need tweaks | -| Extract content extensions sources | Easy | Just file selection | -| CSS parser stub | Easy | ~10 lines | -| DNR translator in C++ | Easy | ~200 lines, direct port from ObjC | -| main.cpp CLI | Easy | ~100 lines | -| Fix compilation issues | Medium | Missing includes, platform ifdefs | -| CI pipeline | Easy | Standard GitHub Actions | -| **Total** | **~2-3 days** | Most time on WTF build integration | - -## Risks - -1. **WTF standalone build complexity** — WTF generates headers (`wtf/PlatformHave.h`, `wtf/PlatformEnable.h`) via CMake. Getting this right outside the full WebKit build is the main risk. -2. **ContentExtensionParser CSS dependency** — if stubbing isn't clean enough, may need to extract more WebCore CSS code. -3. **Platform ifdefs** — content extensions code has `#if ENABLE(CONTENT_EXTENSIONS)` guards everywhere. Need to ensure this is defined. -4. **Static linking on macOS** — fully static binaries aren't supported on macOS (system libraries must be dynamic). On macOS it would be "mostly static" with dynamic libc/libSystem. On Linux, fully static with musl is possible. - -## Alternative: simpler approach - -If the CMake extraction proves too painful, a simpler alternative: - -1. Build WebKit GTK port on Linux (`Tools/Scripts/build-webkit --gtk`) -2. Write the tool as a GLib/C++ program linking against the built WebCore -3. Distribute the binary + libWebCore.so - -This trades binary size and portability for build simplicity, since the GTK port's CMake already handles all the dependency resolution. From 442c02175074a4bfa5a20ca08cabcf0cb1bce214 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 18:51:41 +0200 Subject: [PATCH 05/23] Add CMake build and GitHub Actions CI for cross-platform validate-dnr-rules CMakeLists.txt builds the tool using WebKit's own CMake infrastructure for WTF, and compiles the content extension source files directly from the tree. Stubs provide minimal WTF assertion helpers and YARR Unicode property functions that aren't needed for DNR pattern validation. GitHub Actions workflow builds on both macOS (arm64) and Linux (x64). --- .github/workflows/validate-dnr-rules.yml | 61 +++++++++++++++++++++ ghostery/validate-dnr-rules/CMakeLists.txt | 62 ++++++++++++++++++++++ ghostery/validate-dnr-rules/build.sh | 33 +++++++----- ghostery/validate-dnr-rules/src/stubs.cpp | 58 ++++++++++++++++++++ 4 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/validate-dnr-rules.yml create mode 100644 ghostery/validate-dnr-rules/CMakeLists.txt create mode 100644 ghostery/validate-dnr-rules/src/stubs.cpp diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml new file mode 100644 index 000000000000..f87e551cc189 --- /dev/null +++ b/.github/workflows/validate-dnr-rules.yml @@ -0,0 +1,61 @@ +name: Build validate-dnr-rules + +on: + push: + branches: [ghostery/*] + paths: + - 'ghostery/validate-dnr-rules/**' + - 'Source/WebCore/contentextensions/**' + - '.github/workflows/validate-dnr-rules.yml' + workflow_dispatch: + +jobs: + build: + strategy: + matrix: + include: + - os: macos-latest + name: macos-arm64 + - os: ubuntu-latest + name: linux-x64 + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build libicu-dev pkg-config \ + ruby ruby-dev libglib2.0-dev unifdef + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: brew install cmake ninja icu4c pkg-config + + - name: Configure + run: | + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DPORT=GTK \ + -DENABLE_WEBCORE=OFF \ + -DENABLE_WEBKIT=OFF \ + -DENABLE_TOOLS=OFF \ + ghostery/validate-dnr-rules + env: + CMAKE_PREFIX_PATH: ${{ runner.os == 'macOS' && '/opt/homebrew/opt/icu4c' || '' }} + + - name: Build + run: cmake --build build --target validate-dnr-rules + + - name: Test + run: | + echo '[{"id":1,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"ad[0-9]{2}\\.js"}}]' > /tmp/test-rules.json + ./build/validate-dnr-rules /tmp/test-rules.json + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: validate-dnr-rules-${{ matrix.name }} + path: build/validate-dnr-rules diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt new file mode 100644 index 000000000000..ac36b5156849 --- /dev/null +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.20) +project(validate-dnr-rules CXX C) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(WEBKIT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..") + +# Use WebKit's CMake infrastructure for WTF +set(WTF_DIR "${WEBKIT_ROOT}/Source/WTF") +set(JavaScriptCore_SCRIPTS_DIR "${WEBKIT_ROOT}/Source/JavaScriptCore/Scripts") + +# Include WebKit's top-level CMake options +include("${WEBKIT_ROOT}/Source/cmake/WebKitCommon.cmake" OPTIONAL) + +# Build WTF as a dependency +if (NOT TARGET WTF) + add_subdirectory("${WEBKIT_ROOT}/Source/WTF" "${CMAKE_BINARY_DIR}/WTF") +endif() + +set(CE_DIR "${WEBKIT_ROOT}/Source/WebCore/contentextensions") +set(JSC_DIR "${WEBKIT_ROOT}/Source/JavaScriptCore") + +# Content extension sources (compiled from the WebKit tree, not copied) +set(CE_SOURCES + ${CE_DIR}/URLFilterParser.cpp + ${CE_DIR}/CombinedURLFilters.cpp + ${CE_DIR}/CombinedFiltersAlphabet.cpp + ${CE_DIR}/NFA.cpp +) + +# Our tool sources +set(TOOL_SOURCES + src/main.cpp + src/stubs.cpp +) + +add_executable(validate-dnr-rules ${TOOL_SOURCES} ${CE_SOURCES}) + +target_compile_definitions(validate-dnr-rules PRIVATE + ENABLE_CONTENT_EXTENSIONS=1 + WEBCORE_EXPORT= + WEBCORE_TESTSUPPORT_EXPORT= + STATICALLY_LINKED_WITH_WTF=1 +) + +target_include_directories(validate-dnr-rules PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CE_DIR} +) + +# Create include symlinks so and resolve +set(INCLUDE_BRIDGE_DIR "${CMAKE_BINARY_DIR}/include-bridge") +file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}") +file(CREATE_LINK "${CE_DIR}" "${INCLUDE_BRIDGE_DIR}/WebCore" SYMBOLIC) +file(CREATE_LINK "${JSC_DIR}" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore" SYMBOLIC) +target_include_directories(validate-dnr-rules SYSTEM PRIVATE ${INCLUDE_BRIDGE_DIR}) + +target_link_libraries(validate-dnr-rules PRIVATE WTF) + +find_package(ICU REQUIRED COMPONENTS uc) +target_link_libraries(validate-dnr-rules PRIVATE ICU::uc) diff --git a/ghostery/validate-dnr-rules/build.sh b/ghostery/validate-dnr-rules/build.sh index fb8a61696a74..a2fb78e778f0 100755 --- a/ghostery/validate-dnr-rules/build.sh +++ b/ghostery/validate-dnr-rules/build.sh @@ -4,19 +4,30 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" WEBKIT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" BUILD_DIR="${SCRIPT_DIR}/build" -BDIR="${WEBKIT_ROOT}/WebKitBuild/Debug" SDIR="${WEBKIT_ROOT}/Source" CE_DIR="${SDIR}/WebCore/contentextensions" -if [ ! -d "$BDIR/usr/local/include/wtf" ]; then - echo "Error: WebKit not built. Run: Tools/Scripts/build-webkit --debug" >&2 - exit 1 +# Detect platform +if [[ "$(uname)" == "Darwin" ]]; then + BDIR="${WEBKIT_ROOT}/WebKitBuild/Debug" + if [ ! -d "$BDIR/usr/local/include/wtf" ]; then + echo "Error: WebKit not built. Run: Tools/Scripts/build-webkit --debug" >&2 + exit 1 + fi + WTF_INCLUDE="$BDIR/usr/local/include" + JSC_INCLUDE="$BDIR/JavaScriptCore.framework/PrivateHeaders" + ICU_FLAGS="-licucore" +else + # Linux: use WebKit source tree headers directly + WTF_INCLUDE="$SDIR/WTF" + JSC_INCLUDE="$SDIR/JavaScriptCore" + ICU_FLAGS="$(pkg-config --libs icu-uc 2>/dev/null || echo '-licuuc -licudata')" fi mkdir -p "$BUILD_DIR" /tmp/validate-dnr-includes ln -sfn "$CE_DIR" /tmp/validate-dnr-includes/WebCore -ln -sfn "$BDIR/JavaScriptCore.framework/PrivateHeaders" /tmp/validate-dnr-includes/JavaScriptCore +ln -sfn "$JSC_INCLUDE" /tmp/validate-dnr-includes/JavaScriptCore CC_FLAGS=( -std=c++2b @@ -24,7 +35,7 @@ CC_FLAGS=( -fno-exceptions -O2 -I "$SCRIPT_DIR/src" - -I "$BDIR/usr/local/include" + -I "$WTF_INCLUDE" -isystem /tmp/validate-dnr-includes -I "$CE_DIR" -DENABLE_CONTENT_EXTENSIONS=1 @@ -36,6 +47,7 @@ SOURCES=( "$CE_DIR/CombinedURLFilters.cpp" "$CE_DIR/CombinedFiltersAlphabet.cpp" "$CE_DIR/NFA.cpp" + "$SCRIPT_DIR/src/stubs.cpp" "$SCRIPT_DIR/src/main.cpp" ) @@ -49,13 +61,8 @@ done echo "Linking..." clang++ -o "$BUILD_DIR/validate-dnr-rules" "${OBJECTS[@]}" \ - -F "$BDIR" \ - -framework JavaScriptCore \ - -licucore \ - -Wl,-rpath,"$BDIR" \ + $ICU_FLAGS \ -lc++ -DYLD_FRAMEWORK_PATH="$BDIR" "$BUILD_DIR/validate-dnr-rules" --help 2>/dev/null || true -echo "" echo "Built: $BUILD_DIR/validate-dnr-rules" -echo "Run with: DYLD_FRAMEWORK_PATH=$BDIR $BUILD_DIR/validate-dnr-rules " +echo "Binary size: $(du -h "$BUILD_DIR/validate-dnr-rules" | cut -f1)" diff --git a/ghostery/validate-dnr-rules/src/stubs.cpp b/ghostery/validate-dnr-rules/src/stubs.cpp new file mode 100644 index 000000000000..6484ef5045e9 --- /dev/null +++ b/ghostery/validate-dnr-rules/src/stubs.cpp @@ -0,0 +1,58 @@ +#include "config.h" +#include +#include +#include +#include +#include + +// WTF assertion stubs — these are normally provided by libWTF +extern "C" { + +void WTFCrash() +{ + fprintf(stderr, "WTFCrash\n"); + abort(); +} + +void WTFCrashWithSecurityImplication() +{ + fprintf(stderr, "WTFCrashWithSecurityImplication\n"); + abort(); +} + +void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) +{ + fprintf(stderr, "ASSERTION FAILED: %s (%s:%d %s)\n", assertion, file, line, function); +} + +void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) +{ + fprintf(stderr, "ASSERTION FAILED: %s (%s:%d %s)\n", assertion, file, line, function); +} + +void WTFReportBacktrace() +{ +} + +} // extern "C" + +// YARR Unicode property stubs — these are only needed for \p{} property +// escapes which DNR regexFilter patterns never use. +namespace JSC::Yarr { + +bool characterClassMayContainStrings(BuiltInCharacterClassID) +{ + return false; +} + +std::optional unicodeMatchProperty(WTF::String, CompileMode) +{ + return std::nullopt; +} + +std::optional unicodeMatchPropertyValue(WTF::String, WTF::String) +{ + return std::nullopt; +} + +} // namespace JSC::Yarr From db472894cca1417f2a136e4d1d01c48189c370c2 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 18:57:40 +0200 Subject: [PATCH 06/23] Fix CMake module path and split CI configure per platform --- .github/workflows/validate-dnr-rules.yml | 17 ++++++++++++----- ghostery/validate-dnr-rules/CMakeLists.txt | 16 +++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index f87e551cc189..46f828fa17b0 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -12,6 +12,7 @@ on: jobs: build: strategy: + fail-fast: false matrix: include: - os: macos-latest @@ -34,17 +35,23 @@ jobs: if: runner.os == 'macOS' run: brew install cmake ninja icu4c pkg-config - - name: Configure + - name: Configure (Linux) + if: runner.os == 'Linux' run: | cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DPORT=GTK \ - -DENABLE_WEBCORE=OFF \ - -DENABLE_WEBKIT=OFF \ - -DENABLE_TOOLS=OFF \ + ghostery/validate-dnr-rules + + - name: Configure (macOS) + if: runner.os == 'macOS' + run: | + cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DPORT=Mac \ ghostery/validate-dnr-rules env: - CMAKE_PREFIX_PATH: ${{ runner.os == 'macOS' && '/opt/homebrew/opt/icu4c' || '' }} + CMAKE_PREFIX_PATH: /opt/homebrew/opt/icu4c - name: Build run: cmake --build build --target validate-dnr-rules diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt index ac36b5156849..3c178adf5720 100644 --- a/ghostery/validate-dnr-rules/CMakeLists.txt +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -1,17 +1,23 @@ cmake_minimum_required(VERSION 3.20) + +set(WEBKIT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..") + +# Set up WebKit's CMake module path before project() so platform detection works +set(CMAKE_MODULE_PATH "${WEBKIT_ROOT}/Source/cmake" ${CMAKE_MODULE_PATH}) +list(APPEND CMAKE_MODULE_PATH "${WEBKIT_ROOT}/Source/cmake") + project(validate-dnr-rules CXX C) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(WEBKIT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..") - -# Use WebKit's CMake infrastructure for WTF +# WebKit build system variables set(WTF_DIR "${WEBKIT_ROOT}/Source/WTF") +set(WTF_SCRIPTS_DIR "${WTF_DIR}/wtf/Scripts") set(JavaScriptCore_SCRIPTS_DIR "${WEBKIT_ROOT}/Source/JavaScriptCore/Scripts") -# Include WebKit's top-level CMake options -include("${WEBKIT_ROOT}/Source/cmake/WebKitCommon.cmake" OPTIONAL) +# Include WebKit's common cmake (handles platform detection, find_package, etc.) +include(WebKitCommon) # Build WTF as a dependency if (NOT TARGET WTF) From 4c746795d21b4ca10709b5cdcf4b64bc811b5aae Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 19:03:05 +0200 Subject: [PATCH 07/23] Build validate-dnr-rules via WebKit's root CMake with shallow checkout Configure from the WebKit root so the full CMake infrastructure (WTF, platform detection, ICU) is available. Add our tool as a subdirectory of the root CMakeLists.txt. CI uses fetch-depth: 1 for fast checkout and disables WebCore/WebKit/Tools to only build WTF + our tool. --- .github/workflows/validate-dnr-rules.yml | 33 +++++++------- CMakeLists.txt | 7 +++ ghostery/validate-dnr-rules/CMakeLists.txt | 51 +++++++--------------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 46f828fa17b0..ba837af43774 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -6,6 +6,8 @@ on: paths: - 'ghostery/validate-dnr-rules/**' - 'Source/WebCore/contentextensions/**' + - 'Source/WTF/**' + - 'CMakeLists.txt' - '.github/workflows/validate-dnr-rules.yml' workflow_dispatch: @@ -17,41 +19,41 @@ jobs: include: - os: macos-latest name: macos-arm64 + port: Mac - os: ubuntu-latest name: linux-x64 + port: GTK runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 1 - name: Install dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y cmake ninja-build libicu-dev pkg-config \ - ruby ruby-dev libglib2.0-dev unifdef + sudo apt-get install -y cmake ninja-build pkg-config \ + libicu-dev libglib2.0-dev unifdef ruby - name: Install dependencies (macOS) if: runner.os == 'macOS' run: brew install cmake ninja icu4c pkg-config - - name: Configure (Linux) - if: runner.os == 'Linux' - run: | - cmake -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DPORT=GTK \ - ghostery/validate-dnr-rules - - - name: Configure (macOS) - if: runner.os == 'macOS' + - name: Configure run: | cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ - -DPORT=Mac \ - ghostery/validate-dnr-rules + -DPORT=${{ matrix.port }} \ + -DENABLE_TOOLS=OFF \ + -DENABLE_API_TESTS=OFF \ + -DENABLE_WEBCORE=OFF \ + -DENABLE_WEBKIT=OFF \ + -DENABLE_CONTENT_EXTENSIONS=ON \ + . env: - CMAKE_PREFIX_PATH: /opt/homebrew/opt/icu4c + CMAKE_PREFIX_PATH: ${{ runner.os == 'macOS' && '/opt/homebrew/opt/icu4c' || '' }} - name: Build run: cmake --build build --target validate-dnr-rules @@ -59,6 +61,7 @@ jobs: - name: Test run: | echo '[{"id":1,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"ad[0-9]{2}\\.js"}}]' > /tmp/test-rules.json + echo '[{"id":2,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"(?:ads|tracking)\\.com"}}]' >> /tmp/test-rules.json ./build/validate-dnr-rules /tmp/test-rules.json - name: Upload artifact diff --git a/CMakeLists.txt b/CMakeLists.txt index e3a915b46e63..31ac73a37c32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,13 @@ if (DEVELOPER_MODE) add_subdirectory(PerformanceTests) endif () +# ----------------------------------------------------------------------------- +# Ghostery tools +# ----------------------------------------------------------------------------- +if (EXISTS "${CMAKE_SOURCE_DIR}/ghostery/validate-dnr-rules/CMakeLists.txt") + add_subdirectory(ghostery/validate-dnr-rules) +endif () + # ----------------------------------------------------------------------------- # Print the features list last, for maximum visibility. # ----------------------------------------------------------------------------- diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt index 3c178adf5720..5f0afbbcfd2c 100644 --- a/ghostery/validate-dnr-rules/CMakeLists.txt +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -1,33 +1,15 @@ -cmake_minimum_required(VERSION 3.20) +# This file is included from the WebKit root CMakeLists.txt via add_subdirectory. +# It expects the full WebKit CMake infrastructure to be available (WTF target, etc.) +# +# Build with: +# cmake -B build -G Ninja -DPORT=GTK -DENABLE_TOOLS=OFF -DENABLE_WEBCORE=OFF \ +# -DENABLE_WEBKIT=OFF -DENABLE_CONTENT_EXTENSIONS=ON +# cmake --build build --target validate-dnr-rules + +set(CE_DIR "${CMAKE_SOURCE_DIR}/Source/WebCore/contentextensions") +set(JSC_DIR "${CMAKE_SOURCE_DIR}/Source/JavaScriptCore") +set(TOOL_DIR "${CMAKE_CURRENT_SOURCE_DIR}") -set(WEBKIT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..") - -# Set up WebKit's CMake module path before project() so platform detection works -set(CMAKE_MODULE_PATH "${WEBKIT_ROOT}/Source/cmake" ${CMAKE_MODULE_PATH}) -list(APPEND CMAKE_MODULE_PATH "${WEBKIT_ROOT}/Source/cmake") - -project(validate-dnr-rules CXX C) - -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# WebKit build system variables -set(WTF_DIR "${WEBKIT_ROOT}/Source/WTF") -set(WTF_SCRIPTS_DIR "${WTF_DIR}/wtf/Scripts") -set(JavaScriptCore_SCRIPTS_DIR "${WEBKIT_ROOT}/Source/JavaScriptCore/Scripts") - -# Include WebKit's common cmake (handles platform detection, find_package, etc.) -include(WebKitCommon) - -# Build WTF as a dependency -if (NOT TARGET WTF) - add_subdirectory("${WEBKIT_ROOT}/Source/WTF" "${CMAKE_BINARY_DIR}/WTF") -endif() - -set(CE_DIR "${WEBKIT_ROOT}/Source/WebCore/contentextensions") -set(JSC_DIR "${WEBKIT_ROOT}/Source/JavaScriptCore") - -# Content extension sources (compiled from the WebKit tree, not copied) set(CE_SOURCES ${CE_DIR}/URLFilterParser.cpp ${CE_DIR}/CombinedURLFilters.cpp @@ -35,10 +17,9 @@ set(CE_SOURCES ${CE_DIR}/NFA.cpp ) -# Our tool sources set(TOOL_SOURCES - src/main.cpp - src/stubs.cpp + ${TOOL_DIR}/src/main.cpp + ${TOOL_DIR}/src/stubs.cpp ) add_executable(validate-dnr-rules ${TOOL_SOURCES} ${CE_SOURCES}) @@ -51,12 +32,12 @@ target_compile_definitions(validate-dnr-rules PRIVATE ) target_include_directories(validate-dnr-rules PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/src + ${TOOL_DIR}/src ${CE_DIR} ) -# Create include symlinks so and resolve -set(INCLUDE_BRIDGE_DIR "${CMAKE_BINARY_DIR}/include-bridge") +# Create include bridge so and resolve +set(INCLUDE_BRIDGE_DIR "${CMAKE_BINARY_DIR}/validate-dnr-includes") file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}") file(CREATE_LINK "${CE_DIR}" "${INCLUDE_BRIDGE_DIR}/WebCore" SYMBOLIC) file(CREATE_LINK "${JSC_DIR}" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore" SYMBOLIC) From 1c49702afb01c8c862e7d7e7951141ba9cca2987 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 19:07:49 +0200 Subject: [PATCH 08/23] Install full GTK build deps for Linux CI --- .github/workflows/validate-dnr-rules.yml | 53 ++++++++++++++---------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index ba837af43774..6fa5b58033d6 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -17,12 +17,8 @@ jobs: fail-fast: false matrix: include: - - os: macos-latest - name: macos-arm64 - port: Mac - - os: ubuntu-latest + - os: ubuntu-24.04 name: linux-x64 - port: GTK runs-on: ${{ matrix.os }} steps: @@ -30,40 +26,53 @@ jobs: with: fetch-depth: 1 - - name: Install dependencies (Linux) - if: runner.os == 'Linux' + - name: Install WebKit GTK build dependencies run: | sudo apt-get update - sudo apt-get install -y cmake ninja-build pkg-config \ - libicu-dev libglib2.0-dev unifdef ruby - - - name: Install dependencies (macOS) - if: runner.os == 'macOS' - run: brew install cmake ninja icu4c pkg-config + sudo sed -i '/^Types: deb$/s/$/\nTypes: deb-src/' /etc/apt/sources.list.d/ubuntu.sources || true + sudo apt-get update + sudo apt-get build-dep -y webkit2gtk-4.1 || true + sudo apt-get install -y cmake ninja-build libicu-dev pkg-config \ + unifdef ruby libglib2.0-dev libcairo2-dev libharfbuzz-dev \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + libsoup-3.0-dev libepoxy-dev libwpe-1.0-dev \ + libwpebackend-fdo-1.0-dev libxslt1-dev libmanette-0.2-dev \ + libsecret-1-dev libtasn1-6-dev libsystemd-dev \ + libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ + libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ + libdrm-dev libgbm-dev libinput-dev libudev-dev wayland-protocols \ + libwayland-dev gtk-doc-tools libgtk-4-dev libenchant-2-dev - name: Configure run: | cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ - -DPORT=${{ matrix.port }} \ + -DPORT=GTK \ -DENABLE_TOOLS=OFF \ -DENABLE_API_TESTS=OFF \ - -DENABLE_WEBCORE=OFF \ - -DENABLE_WEBKIT=OFF \ - -DENABLE_CONTENT_EXTENSIONS=ON \ . - env: - CMAKE_PREFIX_PATH: ${{ runner.os == 'macOS' && '/opt/homebrew/opt/icu4c' || '' }} - - name: Build + - name: Build validate-dnr-rules run: cmake --build build --target validate-dnr-rules - name: Test run: | - echo '[{"id":1,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"ad[0-9]{2}\\.js"}}]' > /tmp/test-rules.json - echo '[{"id":2,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"(?:ads|tracking)\\.com"}}]' >> /tmp/test-rules.json + cat > /tmp/test-rules.json << 'RULES' + [ + {"id":1,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"ad[0-9]{2}\\.js"}}, + {"id":2,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"(?:ads|tracking)\\.com"}}, + {"id":3,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"tracker\\d+\\.js"}}, + {"id":4,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"pixel\\b"}} + ] + RULES ./build/validate-dnr-rules /tmp/test-rules.json + - name: Binary info + run: | + file build/validate-dnr-rules + du -h build/validate-dnr-rules + ldd build/validate-dnr-rules || true + - name: Upload artifact uses: actions/upload-artifact@v4 with: From 871edcb5013af98eb715c68304bbbe2b57124005 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 19:10:54 +0200 Subject: [PATCH 09/23] Use WebKit's own install-dependencies script for Linux CI --- .github/workflows/validate-dnr-rules.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 6fa5b58033d6..f9b8a69fa737 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -27,21 +27,7 @@ jobs: fetch-depth: 1 - name: Install WebKit GTK build dependencies - run: | - sudo apt-get update - sudo sed -i '/^Types: deb$/s/$/\nTypes: deb-src/' /etc/apt/sources.list.d/ubuntu.sources || true - sudo apt-get update - sudo apt-get build-dep -y webkit2gtk-4.1 || true - sudo apt-get install -y cmake ninja-build libicu-dev pkg-config \ - unifdef ruby libglib2.0-dev libcairo2-dev libharfbuzz-dev \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libsoup-3.0-dev libepoxy-dev libwpe-1.0-dev \ - libwpebackend-fdo-1.0-dev libxslt1-dev libmanette-0.2-dev \ - libsecret-1-dev libtasn1-6-dev libsystemd-dev \ - libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ - libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ - libdrm-dev libgbm-dev libinput-dev libudev-dev wayland-protocols \ - libwayland-dev gtk-doc-tools libgtk-4-dev libenchant-2-dev + run: yes | Tools/gtk/install-dependencies - name: Configure run: | From fa5b3fd6dc917f2e3bdfed76ef00abce206f22a4 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 19:35:52 +0200 Subject: [PATCH 10/23] Fix Linux deps and add CMake build cache --- .github/workflows/validate-dnr-rules.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index f9b8a69fa737..1ee9da53eb11 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -27,7 +27,27 @@ jobs: fetch-depth: 1 - name: Install WebKit GTK build dependencies - run: yes | Tools/gtk/install-dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + cmake ninja-build pkg-config unifdef ruby \ + libicu-dev libglib2.0-dev libcairo2-dev libharfbuzz-dev \ + libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + libsoup-3.0-dev libepoxy-dev libwpe-1.0-dev \ + libwpebackend-fdo-1.0-dev libxslt1-dev libmanette-0.2-dev \ + libsecret-1-dev libtasn1-6-dev libsystemd-dev \ + libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ + libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ + libdrm-dev libgbm-dev libinput-dev libudev-dev wayland-protocols \ + libwayland-dev libgtk-4-dev libenchant-2-dev + + - name: Cache CMake build + uses: actions/cache@v4 + with: + path: build + key: cmake-${{ matrix.name }}-${{ hashFiles('Source/WTF/**', 'Source/WebCore/contentextensions/**', 'ghostery/validate-dnr-rules/**') }} + restore-keys: | + cmake-${{ matrix.name }}- - name: Configure run: | From ed1983b214d2fff138acdc73065a8d1b82677698 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 21:20:35 +0200 Subject: [PATCH 11/23] Remove unavailable WPE packages from Linux CI --- .github/workflows/validate-dnr-rules.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 1ee9da53eb11..a72a600686ae 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -26,15 +26,16 @@ jobs: with: fetch-depth: 1 - - name: Install WebKit GTK build dependencies + - name: Install build dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ cmake ninja-build pkg-config unifdef ruby \ libicu-dev libglib2.0-dev libcairo2-dev libharfbuzz-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libsoup-3.0-dev libepoxy-dev libwpe-1.0-dev \ - libwpebackend-fdo-1.0-dev libxslt1-dev libmanette-0.2-dev \ + libgstreamer-plugins-bad1.0-dev \ + libsoup-3.0-dev libepoxy-dev \ + libxslt1-dev libmanette-0.2-dev \ libsecret-1-dev libtasn1-6-dev libsystemd-dev \ libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ From 676fc294fe3634e480d913a3db43d704fd1afdab Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 21:24:56 +0200 Subject: [PATCH 12/23] Add missing ATSPI and gi-docgen deps for GTK configure --- .github/workflows/validate-dnr-rules.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index a72a600686ae..760c487c49ac 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -40,7 +40,8 @@ jobs: libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ libdrm-dev libgbm-dev libinput-dev libudev-dev wayland-protocols \ - libwayland-dev libgtk-4-dev libenchant-2-dev + libwayland-dev libgtk-4-dev libenchant-2-dev \ + libatspi2.0-dev gi-docgen - name: Cache CMake build uses: actions/cache@v4 From a43b999d592509444725cde9dfa5997589312a3b Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 22:09:08 +0200 Subject: [PATCH 13/23] Disable optional GTK features to minimize CI dependencies --- .github/workflows/validate-dnr-rules.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 760c487c49ac..8e9cff8d1727 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -58,6 +58,13 @@ jobs: -DPORT=GTK \ -DENABLE_TOOLS=OFF \ -DENABLE_API_TESTS=OFF \ + -DENABLE_SPEECH_SYNTHESIS=OFF \ + -DENABLE_DOCUMENTATION=OFF \ + -DENABLE_GAMEPAD=OFF \ + -DENABLE_BUBBLEWRAP_SANDBOX=OFF \ + -DENABLE_JOURNALD_LOG=OFF \ + -DUSE_AVIF=OFF \ + -DUSE_JPEGXL=OFF \ . - name: Build validate-dnr-rules From 2b5e46c686fc90d44a0d8b79c7f2de9d4df9b821 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Fri, 10 Apr 2026 22:39:15 +0200 Subject: [PATCH 14/23] =?UTF-8?q?Use=20JSCOnly=20port=20for=20CI=20?= =?UTF-8?q?=E2=80=94=20only=20needs=20ICU,=20no=20GTK/platform=20deps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JSCOnly port builds WTF + JavaScriptCore without any platform libraries. Dependencies: cmake, ninja, ruby, unifdef, libicu-dev. That's it. --- .github/workflows/validate-dnr-rules.yml | 28 ++++-------------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 8e9cff8d1727..4846a9f3f1af 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -26,22 +26,11 @@ jobs: with: fetch-depth: 1 - - name: Install build dependencies + - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ - cmake ninja-build pkg-config unifdef ruby \ - libicu-dev libglib2.0-dev libcairo2-dev libharfbuzz-dev \ - libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ - libgstreamer-plugins-bad1.0-dev \ - libsoup-3.0-dev libepoxy-dev \ - libxslt1-dev libmanette-0.2-dev \ - libsecret-1-dev libtasn1-6-dev libsystemd-dev \ - libgcrypt20-dev libwebp-dev liblcms2-dev libopenjp2-7-dev \ - libavif-dev libjxl-dev libgirepository1.0-dev gobject-introspection \ - libdrm-dev libgbm-dev libinput-dev libudev-dev wayland-protocols \ - libwayland-dev libgtk-4-dev libenchant-2-dev \ - libatspi2.0-dev gi-docgen + cmake ninja-build pkg-config ruby unifdef libicu-dev - name: Cache CMake build uses: actions/cache@v4 @@ -55,19 +44,10 @@ jobs: run: | cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ - -DPORT=GTK \ - -DENABLE_TOOLS=OFF \ - -DENABLE_API_TESTS=OFF \ - -DENABLE_SPEECH_SYNTHESIS=OFF \ - -DENABLE_DOCUMENTATION=OFF \ - -DENABLE_GAMEPAD=OFF \ - -DENABLE_BUBBLEWRAP_SANDBOX=OFF \ - -DENABLE_JOURNALD_LOG=OFF \ - -DUSE_AVIF=OFF \ - -DUSE_JPEGXL=OFF \ + -DPORT=JSCOnly \ . - - name: Build validate-dnr-rules + - name: Build run: cmake --build build --target validate-dnr-rules - name: Test From 5c4b19b46b7a16430e2c5f03f26c8701ba7fbd12 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 01:04:32 +0200 Subject: [PATCH 15/23] Fix WTF include paths for CMake build --- ghostery/validate-dnr-rules/CMakeLists.txt | 4 ++++ ghostery/validate-dnr-rules/src/config.h | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt index 5f0afbbcfd2c..e5bb2f225331 100644 --- a/ghostery/validate-dnr-rules/CMakeLists.txt +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -34,6 +34,10 @@ target_compile_definitions(validate-dnr-rules PRIVATE target_include_directories(validate-dnr-rules PRIVATE ${TOOL_DIR}/src ${CE_DIR} + ${WTF_DIR} + ${WTF_DIR}/wtf + ${CMAKE_BINARY_DIR} + ${WTF_DERIVED_SOURCES_DIR} ) # Create include bridge so and resolve diff --git a/ghostery/validate-dnr-rules/src/config.h b/ghostery/validate-dnr-rules/src/config.h index d3afe7022f69..d128481e8bee 100644 --- a/ghostery/validate-dnr-rules/src/config.h +++ b/ghostery/validate-dnr-rules/src/config.h @@ -1,10 +1,17 @@ #pragma once +#ifdef HAVE_CONFIG_H +#include "cmakeconfig.h" +#endif + #include #include #include #include +#undef ENABLE_CONTENT_EXTENSIONS #define ENABLE_CONTENT_EXTENSIONS 1 + +#ifndef WEBCORE_EXPORT #define WEBCORE_EXPORT -#define WEBCORE_TESTSUPPORT_EXPORT +#endif From 5a48b345dd3a40c34f6ca9de9dbe3767628741e5 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 01:10:32 +0200 Subject: [PATCH 16/23] Fix JavaScriptCore include bridge to point to yarr/ subdirectory --- ghostery/validate-dnr-rules/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt index e5bb2f225331..bdf2398e7c50 100644 --- a/ghostery/validate-dnr-rules/CMakeLists.txt +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -44,8 +44,11 @@ target_include_directories(validate-dnr-rules PRIVATE set(INCLUDE_BRIDGE_DIR "${CMAKE_BINARY_DIR}/validate-dnr-includes") file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}") file(CREATE_LINK "${CE_DIR}" "${INCLUDE_BRIDGE_DIR}/WebCore" SYMBOLIC) -file(CREATE_LINK "${JSC_DIR}" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore" SYMBOLIC) -target_include_directories(validate-dnr-rules SYSTEM PRIVATE ${INCLUDE_BRIDGE_DIR}) +file(CREATE_LINK "${JSC_DIR}/yarr" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore" SYMBOLIC) +target_include_directories(validate-dnr-rules SYSTEM PRIVATE + ${INCLUDE_BRIDGE_DIR} + ${JSC_DIR} +) target_link_libraries(validate-dnr-rules PRIVATE WTF) From 88ff23886f81a6492488f6bdac3f6ad3dee70664 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 09:11:44 +0200 Subject: [PATCH 17/23] Linux build working: JSCOnly port + system malloc + Gigacage stub Use -DPORT=JSCOnly which only needs ICU (no GTK/Cairo/GStreamer deps). Use -DUSE_SYSTEM_MALLOC=ON to avoid bmalloc dependency. Stub Gigacage::ensureGigacage() and YARR Unicode property functions. Flatten JSC headers into include bridge so resolves correctly from Source/JavaScriptCore/yarr/Yarr.h. Verified in Docker: ubuntu:24.04 with cmake, ninja, g++, libicu-dev. --- .github/workflows/validate-dnr-rules.yml | 13 ++++---- ghostery/validate-dnr-rules/CMakeLists.txt | 32 +++++++++++++++---- ghostery/validate-dnr-rules/src/stubs.cpp | 36 +++------------------- 3 files changed, 37 insertions(+), 44 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 4846a9f3f1af..7c95c07dfc39 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -30,7 +30,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ - cmake ninja-build pkg-config ruby unifdef libicu-dev + cmake ninja-build pkg-config ruby unifdef libicu-dev g++ perl python3 - name: Cache CMake build uses: actions/cache@v4 @@ -45,6 +45,7 @@ jobs: cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DPORT=JSCOnly \ + -DUSE_SYSTEM_MALLOC=ON \ . - name: Build @@ -60,16 +61,16 @@ jobs: {"id":4,"priority":1,"action":{"type":"block"},"condition":{"regexFilter":"pixel\\b"}} ] RULES - ./build/validate-dnr-rules /tmp/test-rules.json + ./build/bin/validate-dnr-rules /tmp/test-rules.json - name: Binary info run: | - file build/validate-dnr-rules - du -h build/validate-dnr-rules - ldd build/validate-dnr-rules || true + file build/bin/validate-dnr-rules + du -h build/bin/validate-dnr-rules + ldd build/bin/validate-dnr-rules || true - name: Upload artifact uses: actions/upload-artifact@v4 with: name: validate-dnr-rules-${{ matrix.name }} - path: build/validate-dnr-rules + path: build/bin/validate-dnr-rules diff --git a/ghostery/validate-dnr-rules/CMakeLists.txt b/ghostery/validate-dnr-rules/CMakeLists.txt index bdf2398e7c50..284c50456449 100644 --- a/ghostery/validate-dnr-rules/CMakeLists.txt +++ b/ghostery/validate-dnr-rules/CMakeLists.txt @@ -25,7 +25,6 @@ set(TOOL_SOURCES add_executable(validate-dnr-rules ${TOOL_SOURCES} ${CE_SOURCES}) target_compile_definitions(validate-dnr-rules PRIVATE - ENABLE_CONTENT_EXTENSIONS=1 WEBCORE_EXPORT= WEBCORE_TESTSUPPORT_EXPORT= STATICALLY_LINKED_WITH_WTF=1 @@ -40,14 +39,35 @@ target_include_directories(validate-dnr-rules PRIVATE ${WTF_DERIVED_SOURCES_DIR} ) -# Create include bridge so and resolve +# Create include bridge so and resolve. +# WebKit headers use flat but files live in subdirs. set(INCLUDE_BRIDGE_DIR "${CMAKE_BINARY_DIR}/validate-dnr-includes") -file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}") -file(CREATE_LINK "${CE_DIR}" "${INCLUDE_BRIDGE_DIR}/WebCore" SYMBOLIC) -file(CREATE_LINK "${JSC_DIR}/yarr" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore" SYMBOLIC) +file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}/WebCore") +file(MAKE_DIRECTORY "${INCLUDE_BRIDGE_DIR}/JavaScriptCore") + +file(GLOB CE_HEADERS "${CE_DIR}/*.h") +foreach(H ${CE_HEADERS}) + get_filename_component(HNAME ${H} NAME) + file(CREATE_LINK "${H}" "${INCLUDE_BRIDGE_DIR}/WebCore/${HNAME}" SYMBOLIC) +endforeach() + +file(GLOB YARR_HEADERS "${JSC_DIR}/yarr/*.h") +foreach(H ${YARR_HEADERS}) + get_filename_component(HNAME ${H} NAME) + file(CREATE_LINK "${H}" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore/${HNAME}" SYMBOLIC) +endforeach() + +# Also link JSC headers from subdirectories that YARR needs +file(GLOB_RECURSE ALL_JSC_HEADERS "${JSC_DIR}/*.h") +foreach(H ${ALL_JSC_HEADERS}) + get_filename_component(HNAME ${H} NAME) + if(NOT EXISTS "${INCLUDE_BRIDGE_DIR}/JavaScriptCore/${HNAME}") + file(CREATE_LINK "${H}" "${INCLUDE_BRIDGE_DIR}/JavaScriptCore/${HNAME}" SYMBOLIC) + endif() +endforeach() + target_include_directories(validate-dnr-rules SYSTEM PRIVATE ${INCLUDE_BRIDGE_DIR} - ${JSC_DIR} ) target_link_libraries(validate-dnr-rules PRIVATE WTF) diff --git a/ghostery/validate-dnr-rules/src/stubs.cpp b/ghostery/validate-dnr-rules/src/stubs.cpp index 6484ef5045e9..e8fe7dd236fd 100644 --- a/ghostery/validate-dnr-rules/src/stubs.cpp +++ b/ghostery/validate-dnr-rules/src/stubs.cpp @@ -1,42 +1,14 @@ #include "config.h" -#include -#include #include #include #include -// WTF assertion stubs — these are normally provided by libWTF -extern "C" { - -void WTFCrash() -{ - fprintf(stderr, "WTFCrash\n"); - abort(); -} - -void WTFCrashWithSecurityImplication() -{ - fprintf(stderr, "WTFCrashWithSecurityImplication\n"); - abort(); -} - -void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) -{ - fprintf(stderr, "ASSERTION FAILED: %s (%s:%d %s)\n", assertion, file, line, function); +// Gigacage stub — not needed when using system malloc +namespace Gigacage { +void ensureGigacage() { } } -void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) -{ - fprintf(stderr, "ASSERTION FAILED: %s (%s:%d %s)\n", assertion, file, line, function); -} - -void WTFReportBacktrace() -{ -} - -} // extern "C" - -// YARR Unicode property stubs — these are only needed for \p{} property +// YARR Unicode property stubs — only needed for \p{} property // escapes which DNR regexFilter patterns never use. namespace JSC::Yarr { From c6d988502eb1a3c4cc5a2caf95bacf8560f87f27 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 18:17:17 +0200 Subject: [PATCH 18/23] Add macOS arm64 build to CI, fix bmalloc stubs for cross-platform Add bmalloc/Gigacage stubs with proper types so the tool links on both Linux and macOS without the full bmalloc library. CI now builds on both ubuntu-24.04 (x64) and macos-14 (arm64). --- .github/workflows/validate-dnr-rules.yml | 17 +++++++++++++---- ghostery/validate-dnr-rules/src/stubs.cpp | 21 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 7c95c07dfc39..8e96406d6ff6 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -19,6 +19,10 @@ jobs: include: - os: ubuntu-24.04 name: linux-x64 + deps: cmake ninja-build pkg-config ruby unifdef libicu-dev g++ perl python3 + - os: macos-14 + name: macos-arm64 + deps: cmake ninja icu4c pkg-config runs-on: ${{ matrix.os }} steps: @@ -26,11 +30,15 @@ jobs: with: fetch-depth: 1 - - name: Install dependencies + - name: Install dependencies (Linux) + if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - cmake ninja-build pkg-config ruby unifdef libicu-dev g++ perl python3 + sudo apt-get install -y --no-install-recommends ${{ matrix.deps }} + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: brew install ${{ matrix.deps }} - name: Cache CMake build uses: actions/cache@v4 @@ -47,6 +55,8 @@ jobs: -DPORT=JSCOnly \ -DUSE_SYSTEM_MALLOC=ON \ . + env: + CMAKE_PREFIX_PATH: ${{ runner.os == 'macOS' && '/opt/homebrew/opt/icu4c' || '' }} - name: Build run: cmake --build build --target validate-dnr-rules @@ -67,7 +77,6 @@ jobs: run: | file build/bin/validate-dnr-rules du -h build/bin/validate-dnr-rules - ldd build/bin/validate-dnr-rules || true - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/ghostery/validate-dnr-rules/src/stubs.cpp b/ghostery/validate-dnr-rules/src/stubs.cpp index e8fe7dd236fd..ed07bc7ff36a 100644 --- a/ghostery/validate-dnr-rules/src/stubs.cpp +++ b/ghostery/validate-dnr-rules/src/stubs.cpp @@ -3,11 +3,30 @@ #include #include -// Gigacage stub — not needed when using system malloc +// Gigacage/bmalloc stubs — not needed when using system malloc +#include +#include +#include + namespace Gigacage { void ensureGigacage() { } } +extern "C" __attribute__((visibility("default"))) bool disablePrimitiveGigacageRequested = false; + +namespace bmalloc::api { +void commitAlignedPhysical(void*, size_t, HeapKind) { } +void decommitAlignedPhysical(void*, size_t, HeapKind) { } +void disableScavenger() { } +void enableMiniMode(bool) { } +void forceEnablePGM(unsigned short) { } +void freeLargeVirtual(void*, size_t, HeapKind) { } +bool isEnabled(HeapKind) { return false; } +void scavenge() { } +void scavengeThisThread() { } +void* tryLargeZeroedMemalignVirtual(size_t, size_t, CompactAllocationMode, HeapKind) { return nullptr; } +} + // YARR Unicode property stubs — only needed for \p{} property // escapes which DNR regexFilter patterns never use. namespace JSC::Yarr { From 2880b64020820d870da2a37c6b7572860b2eb26d Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 18:22:37 +0200 Subject: [PATCH 19/23] Use macos-15 runner for newer Clang with asm constraint support --- .github/workflows/validate-dnr-rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 8e96406d6ff6..3b96cd1b7da6 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -20,7 +20,7 @@ jobs: - os: ubuntu-24.04 name: linux-x64 deps: cmake ninja-build pkg-config ruby unifdef libicu-dev g++ perl python3 - - os: macos-14 + - os: macos-15 name: macos-arm64 deps: cmake ninja icu4c pkg-config runs-on: ${{ matrix.os }} From 1bf5e3e4eb6990eaace77f303c595b7ca951436e Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 18:50:43 +0200 Subject: [PATCH 20/23] Create GitHub Release with binaries on merge to ghostery branch --- .github/workflows/validate-dnr-rules.yml | 54 +++++++++++++++++++----- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 3b96cd1b7da6..3673de682c94 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -2,13 +2,7 @@ name: Build validate-dnr-rules on: push: - branches: [ghostery/*] - paths: - - 'ghostery/validate-dnr-rules/**' - - 'Source/WebCore/contentextensions/**' - - 'Source/WTF/**' - - 'CMakeLists.txt' - - '.github/workflows/validate-dnr-rules.yml' + branches: [ghostery] workflow_dispatch: jobs: @@ -73,13 +67,51 @@ jobs: RULES ./build/bin/validate-dnr-rules /tmp/test-rules.json - - name: Binary info + - name: Prepare artifact run: | - file build/bin/validate-dnr-rules - du -h build/bin/validate-dnr-rules + cp build/bin/validate-dnr-rules validate-dnr-rules-${{ matrix.name }} + chmod +x validate-dnr-rules-${{ matrix.name }} - name: Upload artifact uses: actions/upload-artifact@v4 with: name: validate-dnr-rules-${{ matrix.name }} - path: build/bin/validate-dnr-rules + path: validate-dnr-rules-${{ matrix.name }} + + release: + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + TAG="validate-dnr-rules-$(date +%Y%m%d)-${GITHUB_SHA::8}" + + # Delete existing release with same tag if re-running + gh release delete "$TAG" --yes 2>/dev/null || true + + gh release create "$TAG" \ + --title "validate-dnr-rules $(date +%Y-%m-%d)" \ + --notes "Automated build from commit ${GITHUB_SHA::8}. + + ## Downloads + - **Linux x64**: \`validate-dnr-rules-linux-x64\` + - **macOS arm64**: \`validate-dnr-rules-macos-arm64\` (Intel Macs: run via Rosetta) + + ## Usage + \`\`\` + chmod +x validate-dnr-rules-* + ./validate-dnr-rules-linux-x64 path/to/dnr-rules.json + \`\`\`" \ + artifacts/validate-dnr-rules-linux-x64/validate-dnr-rules-linux-x64 \ + artifacts/validate-dnr-rules-macos-arm64/validate-dnr-rules-macos-arm64 From 99f8d74b7ebba0f38b131a70b1f5c9ae60c9844f Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 18:51:05 +0200 Subject: [PATCH 21/23] Also trigger on ghostery/* branches for testing --- .github/workflows/validate-dnr-rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 3673de682c94..cf1b9299d70f 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -2,7 +2,7 @@ name: Build validate-dnr-rules on: push: - branches: [ghostery] + branches: [ghostery, ghostery/*] workflow_dispatch: jobs: From d34b0477e3e514a90fd3d6bcd26181779bcd86d3 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 18:56:38 +0200 Subject: [PATCH 22/23] Use 'validator' as the release branch name --- .github/workflows/validate-dnr-rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index cf1b9299d70f..5a6590b55eaf 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -2,7 +2,7 @@ name: Build validate-dnr-rules on: push: - branches: [ghostery, ghostery/*] + branches: [validator, ghostery/*] workflow_dispatch: jobs: From 0e04e842323049cb1d1478c6ac80fda0054d7167 Mon Sep 17 00:00:00 2001 From: Krzysztof Modras Date: Sat, 11 Apr 2026 19:52:28 +0200 Subject: [PATCH 23/23] Ad-hoc codesign macOS binary for Apple Silicon Gatekeeper --- .github/workflows/validate-dnr-rules.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/validate-dnr-rules.yml b/.github/workflows/validate-dnr-rules.yml index 5a6590b55eaf..37c02f1d0607 100644 --- a/.github/workflows/validate-dnr-rules.yml +++ b/.github/workflows/validate-dnr-rules.yml @@ -67,6 +67,10 @@ jobs: RULES ./build/bin/validate-dnr-rules /tmp/test-rules.json + - name: Sign binary (macOS) + if: runner.os == 'macOS' + run: codesign --sign - --force build/bin/validate-dnr-rules + - name: Prepare artifact run: | cp build/bin/validate-dnr-rules validate-dnr-rules-${{ matrix.name }}