diff --git a/fix_playwright_smart.py b/fix_playwright_smart.py new file mode 100755 index 00000000..fc0965e7 --- /dev/null +++ b/fix_playwright_smart.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +""" +Smarter script to fix 0-playwright.patch line numbers. +Handles multi-hunk rejects and uses better context matching. +""" + +import re +import os +from pathlib import Path +from difflib import SequenceMatcher + +SKIP_FILES = { + 'dom/base/Navigator.cpp', # API conflict + 'dom/base/Navigator.h', # API conflict +} + +def parse_reject_file(rej_path): + """Parse all hunks from a reject file""" + with open(rej_path, 'r') as f: + content = f.read() + + hunks = [] + current_hunk = [] + + for line in content.split('\n'): + if line.startswith('@@'): + if current_hunk: + hunks.append('\n'.join(current_hunk)) + current_hunk = [line] + elif current_hunk: + current_hunk.append(line) + + if current_hunk: + hunks.append('\n'.join(current_hunk)) + + return hunks + +def parse_hunk(hunk_text): + """Parse a single hunk""" + lines = hunk_text.split('\n') + header = lines[0] + + match = re.match(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@(.*)$', header) + if not match: + return None + + # Extract context lines (non-modified lines around the change) + context_before = [] + context_after = [] + changes = [] + + in_changes = False + for line in lines[1:]: + if not line: + continue + if line.startswith(' '): + if not in_changes and not changes: + context_before.append(line[1:]) + elif in_changes: + context_after.append(line[1:]) + elif line.startswith('+') or line.startswith('-'): + in_changes = True + changes.append(line) + + return { + 'old_line': int(match.group(1)), + 'old_count': int(match.group(2)), + 'new_line': int(match.group(3)), + 'new_count': int(match.group(4)), + 'context': match.group(5), + 'header': header, + 'context_before': context_before, + 'context_after': context_after, + 'changes': changes, + 'full_text': hunk_text + } + +def find_line_in_source(source_lines, context_lines, approximate=False): + """Find where context appears in source file""" + # Try exact match first + for i in range(len(source_lines)): + match = True + for j, ctx in enumerate(context_lines[:5]): # Check first 5 context lines + if i + j >= len(source_lines): + match = False + break + if ctx.strip(): + if ctx.strip() not in source_lines[i + j]: + match = False + break + if match: + return i + 1 # 1-indexed + + # Try approximate matching + if approximate and context_lines: + best_match = None + best_ratio = 0 + search_text = ' '.join(c.strip() for c in context_lines[:5]) + + for i in range(len(source_lines)): + window = ' '.join(source_lines[i:min(i+10, len(source_lines))]) + ratio = SequenceMatcher(None, search_text, window).ratio() + if ratio > best_ratio and ratio > 0.6: + best_ratio = ratio + best_match = i + 1 + + if best_match: + return best_match + + return None + +def update_hunk_in_patch(patch_path, source_file, old_header, new_line, old_count, new_count, context_str): + """Update a single hunk header in the patch file""" + with open(patch_path, 'r') as f: + content = f.read() + + # Find file section + file_start = content.find(f'diff --git a/{source_file}') + if file_start == -1: + return False + + next_file = content.find('\ndiff --git', file_start + 1) + if next_file == -1: + section = content[file_start:] + section_end = len(content) + else: + section = content[file_start:next_file] + section_end = file_start + len(section) + + # Find and replace the old header + header_pos = section.find(old_header) + if header_pos == -1: + return False + + new_header = f'@@ -{new_line},{old_count} +{new_line},{new_count} @@{context_str}' + + # Replace in full content + abs_pos = file_start + header_pos + content = content[:abs_pos] + content[abs_pos:].replace(old_header, new_header, 1) + + with open(patch_path, 'w') as f: + f.write(content) + + return True + +def main(): + print("šŸ” Finding reject files...") + build_dir = Path('camoufox-146.0.1-beta.25') + + rejects = [] + for rej in build_dir.rglob('*.rej'): + source_file = str(rej.relative_to(build_dir)).replace('.rej', '') + if source_file not in SKIP_FILES: + rejects.append((source_file, rej)) + + print(f"Found {len(rejects)} reject files\n") + + fixed = 0 + failed = 0 + + for source_file, rej_path in rejects: + print(f"\nšŸ“ {source_file}") + + source_path = build_dir / source_file + if not source_path.exists(): + print(f" āš ļø Source file not found") + continue + + with open(source_path, 'r') as f: + source_lines = f.readlines() + + hunks = parse_reject_file(rej_path) + print(f" Found {len(hunks)} failed hunk(s)") + + for i, hunk_text in enumerate(hunks): + hunk = parse_hunk(hunk_text) + if not hunk: + print(f" Hunk {i+1}: āŒ Could not parse") + failed += 1 + continue + + # Try to find using context before changes + search_context = hunk['context_before'] if hunk['context_before'] else hunk['context_after'] + if not search_context: + # Use the header context text as fallback + if hunk['context']: + search_context = [hunk['context'].strip()] + + correct_line = find_line_in_source(source_lines, search_context, approximate=True) + + if not correct_line: + print(f" Hunk {i+1} (line {hunk['old_line']}): āŒ Context not found") + failed += 1 + continue + + if correct_line == hunk['old_line']: + print(f" Hunk {i+1} (line {hunk['old_line']}): āœ“ Already correct") + continue + + print(f" Hunk {i+1}: šŸ“ {hunk['old_line']} → {correct_line}", end='') + + if update_hunk_in_patch('patches/playwright/0-playwright.patch', + source_file, + hunk['header'], + correct_line, + hunk['old_count'], + hunk['new_count'], + hunk['context']): + print(" āœ…") + fixed += 1 + else: + print(" āŒ Update failed") + failed += 1 + + print(f"\n{'='*60}") + print(f"āœ… Fixed: {fixed} hunks") + print(f"āŒ Failed: {failed} hunks") + print(f"{'='*60}") + +if __name__ == '__main__': + main() diff --git a/patches/playwright/0-playwright.patch b/patches/playwright/0-playwright.patch index 723fe00b..44241e20 100644 --- a/patches/playwright/0-playwright.patch +++ b/patches/playwright/0-playwright.patch @@ -245,7 +245,7 @@ diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index b42c32133c6490755ae4e1c599255418e6443011..afa502833ba55c94dbd30726d37450c460a69e2b 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h -@@ -205,10 +205,10 @@ struct EmbedderColorSchemes { +@@ -209,10 +209,10 @@ struct EmbedderColorSchemes { FIELD(GVInaudibleAutoplayRequestStatus, GVAutoplayRequestStatus) \ /* ScreenOrientation-related APIs */ \ FIELD(CurrentOrientationAngle, float) \ @@ -1934,7 +1934,7 @@ index 83cef5b52b8f5c3168cd1d59266b4149425647fd..a80e89ec26a7e049c8ed84687ba2cd49 RuntimeService* runtime = RuntimeService::GetService(); if (runtime) { -@@ -1272,8 +1272,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { +@@ -1307,8 +1307,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { } // The navigator overridden properties should have already been read. @@ -3440,7 +3440,7 @@ index 2f4c33f151870bb9233899b186a5eccfaa6f5da6..6acf66e4938e3f9727916824a08d6294 } static bool Read(MessageReader* aReader, paramType* aResult) { -@@ -275,6 +276,7 @@ struct ParamTraits { +@@ -278,6 +278,7 @@ struct ParamTraits { aResult->mExitFrom = Some(static_cast(exitFrom)); } rv = rv && ReadParam(aReader, &aResult->mClickCount);