שני דגלי הפעלה נפוצים בדוגמאות לאוטומציה של הרחבות:
--disable-extensions-except=/path/to/ext --load-extension=/path/to/ext
במתכונים ותיקים מפנים את Puppeteer או Playwright אל Chrome שמותקן במערכת ומעבירים את שני הדגלים האלה.
בגרסת Google Chrome 150 שנבדקה כאן, הדפדפן עלה בלי שגיאת שורת פקודה, אבל שירות ההרחבות התעלם משני הדגלים. האוטומציה התחברה כרגיל, ההרחבה לא הופיעה, והתסמין המאוחר היה timeout של selector במסגרת הבדיקה הזו.
Chrome רשם את הסירוב בשורה אחת, אבל רק אחרי שהפעלנו לוגים ל־stderr.
הממצא המרכזי כאן הוא ההשוואה בין גרסאות בנייה של הדפדפן. שני החלקים המאוחרים יותר הם במפורש הערות על ההרצה: אחד עוסק בהורדות שמופעלות מתוך הרחבה, והשני בנתיב ההתקנה הזה על fixtures מסוג file://. Thunderbit עצמה היא הרחבת Chrome, כך שיש לנו עניין ישיר בבעיה הזו; Thunderbit לא הייתה יעד הבדיקה.
השורה שמסבירה הכול
הוסיפו --enable-logging=stderr והפעילו את Chrome הסטנדרטי עם הדגלים האלה:
הפניה רשמית: קוד שירות ההרחבות של Chromium.
WARNING:chrome/browser/extensions/extension_service.cc:442]
--disable-extensions-except is not allowed in Google Chrome, ignoring.
אם מעבירים רק את --load-extension, מתקבלת אזהרה נפרדת, משורה אחרת באותו קובץ:
WARNING:chrome/browser/extensions/extension_service.cc:420]
--load-extension is not allowed in Google Chrome, ignoring.
לעומת זאת, Chrome for Testing עם אותם דגלים לא מדפיס אף אחת מהאזהרות האלה.
"לא מותר ב־Google Chrome." האזהרה הזו מובילה לפרשנות של כלל שמאפיין build ממותג. היא מוכיחה שב־Google Chrome 150 שנבדק כאן הדגל התעלם, ומציינת גם את מיקום המקור. אבל היא לא מוכיחה לבדה שהגרסה עצמה אינה רלוונטית, ולא חושפת מה בדיוק מבדיל את המימוש.
כל מה שמופיע בהמשך הוא אימות והשלכות.
אימות דרך ההתנהגות בפועל
הרחבת MV3 מינימלית, שנבנתה במיוחד לבדיקה הזו ולא הורדה מאיפשהו, משתמשת ב־content script, popup, מעבר הודעות הלוך ושוב, חילוץ DOM וייצוא דרך chrome.downloads מול fixture מקומי של שלושה מוצרים. מסגרת הבדיקה רושמת שש בדיקות ממוספרות: service worker נרשם; ל־marker של ההרחבה יש מזהה; marker של ה־content script תואם; כפתור ה־popup גלוי; ה־popup מחזיר שלוש שורות; קובץ ה־CSV שנלכד מכיל את השורה הצפויה. בדיקת הסכמה נפרדת משווה בין האותות העצמאיים של service worker ושל ה־page marker.
שלוש גרסאות הבנייה השתמשו באותם דגלי הרחבה מפורשים, באותה תיקיית הרחבה, ב־HTTP fixture, במצב headed persistent-context, ובפרופיל חדש לכל זרוע. הזרוע של Chrome הסטנדרטי רצה דרך channel: 'chrome'; שתי הזרועות שעברו השתמשו ב־executablePath מפורש. מחרוזות הגרסה נקראו חזרה דרך CDP.
| Build | Version reported by the browser | Extension service worker | Content script injected | Full six-check run |
|---|---|---|---|---|
| Chrome for Testing | Chrome/149.0.7827.55 | ✅ | ✅ | PASS 6/6 |
| Stock Google Chrome | Chrome/150.0.7871.187 | ❌ never registered | ❌ | FAIL at step 0 |
| Chrome for Testing | Chrome/151.0.7922.10 | ✅ | ✅ | PASS 6/6 |
סיכומים גולמיים: Chrome for Testing 149, stock Chrome 150, Chrome for Testing 151, ו־אזהרות stderr.
Chrome for Testing 149 נכלל כאן בכוונה: הוא ישן יותר מהגרסה הסטנדרטית שאליה משווים אותו. אם היכולת הייתה נעלמת בגלל קפיצה בגרסה, היינו מצפים שבניין ישן יותר יהיה בצד שעובד, והגרסה הכושלת תהיה החדשה ביותר. בפועל, הגרסה הכושלת יושבת בין שתי גרסאות שעובדות, ולכן זה לא מסתדר עם תהליך פשוט של התקדמות לפי גרסה.
אבל הטבלה לבדה לא מוכיחה שהחסם הוא לפי build, וחשוב לדייק למה. התא הכושל היחיד הוא בו־זמנית גם התא היחיד של Chrome הסטנדרטי וגם התא היחיד של גרסת 150 — כלומר build וגרסה עדיין משולבים לגמרי בעיצוב הזה. שלוש זרועות כן שוללות הסרה מונוטונית; הן לא יכולות לשלול תרחיש של "נשבר ב־150, תוקן ב־151". כדי להגיע למסקנה הזו רק מההתנהגות היה צריך תא שהמכונה הזו לא יכולה לייצר: Chrome for Testing 150, או build ממותג בגרסה אחרת.
האזהרה מחזקת את הפרשנות של build ממותג משום שהיא מציינת במפורש Google Chrome, בעוד ההתנהגות בשלוש הזרועות שוללת רק הסרה מונוטונית פשוטה. כדי להוכיח מנגנון שאינו תלוי בגרסה עדיין היה נדרש או השוואה בין builds באותה גרסה, או ציטוט ממקור/קונפיגורציה.
הארטיפקטים המקושרים לכל build מציגים את ריצת הסיכום של כל זרוע; המאמר הזה לא מפרסם מטריצה מלאה ריצה־אחר־ריצה עבור מספר ההרצות הנוסף, ולכן הוא לא משתמש במספר הזה כראיה עצמאית.
אות אחד לא מספיק כדי לקבוע "זה לא נטען"
הגרסה הראשונה של הבדיקה הזו קבעה אם ההרחבה נטענה לפי marker שה־content script כותב לתוך הדף — וזה גם מה ששימש אותה כדי לקבוע אם ה־content script רץ. קריאה אחת, ששימשה כשתי מדידות. אם ה־marker חסר, אי אפשר לדעת אם "ההרחבה בכלל לא נטענה" או "היא נטענה אבל ה־content script לא הוזרק", ואלה בעיות שונות לגמרי.
הרחבות MV3 מריצות service worker ברקע, ו־Playwright חושף service workers ישירות. זה אות עצמאי: הוא לא נוגע בדף, ולכן אי אפשר לבלבל בינו לבין injection. הבדיקה הנוכחית קוראת את שניהם ובודקת שהם מסכימים.
בכל שלוש הריצות הם אכן הסכימו. ב־Chrome הסטנדרטי לא נרשם service worker בכלל — הצורה החזקה של הטענה. בשתי גרסאות Chrome for Testing ה־worker עלה בכתובת chrome-extension://<id>/background.js עוד לפני שהדף נפתח.
להשתמש ב־Chrome for Testing, שאולי כבר יש לכם
שתי הזרועות שעברו השתמשו ב־Chrome for Testing עם גרסאות 149.0.7827.55 ו־151.0.7922.10. כוונו את executablePath של Playwright אל ה־executable המקובע במקום לפתור את Chrome הסטנדרטי דרך channel: 'chrome'. הפקודה npx playwright install chromium מתקינה build של Chromium שמנוהל על ידי Playwright; גם הוא יכול להתאים לאוטומציה, אבל זה לא אותו label הפצה כמו שתי זרועות Chrome for Testing כאן, והוא לא היה זרוע רביעית בהשוואה הזו.
הפניה רשמית: ההכרזה על Chrome for Testing.
סקירה קשורה: ביקורת הרשאות של Chrome extension scraper.
יש גם יתרון עקיף שמחזיק מעבר לשבירה הספציפית הזו. Chrome הסטנדרטי מתעדכן אוטומטית מתחתיכם, כך שסוויטה שעובדת היום יכולה להיכשל ביום שלישי מסיבות שלא קשורות לשום commit. Chrome for Testing מקובע. לכל דבר שהתוצאה שלו אמורה להישאר בעלת משמעות גם בעוד שלושה חודשים, זה חשוב יותר מנוחות.
הערת הרצה: לכידת הייצוא של ההרחבה
עבור הרחבת scraping, הייצוא הוא כל הסיפור — שם נפלים שדות, קידודים מתעוותים, ונתונים מקוננים משוטחים לא נכון. יש כאן שתי נקודות לא מתועדות, ושתיהן נושכות.
| What the export run recorded | Value |
|---|---|
playwright_download_event_fired | false |
suggestedFilename | null |
| Filename the extension asks for | probe-export.csv |
| Filename that lands in the directory | download.csv |
| File contents | the header and all three rows survive intact |
ב־MV3/Playwright 1.56.0 הזה, chrome.downloads לא הפעיל את אירוע ההורדה של Playwright. כשההרחבה ייצאה CSV דרך ה־API, הקריאה waitForEvent('download') לא הסתיימה. מסגרת הבדיקה לכדה את הקובץ על ידי פתיחת session של CDP, הגדרת התנהגות הורדה בצורה מפורשת, וקריאת תיקיית הפלט:
const cdp = await ctx.newCDPSession(page);
await cdp.send('Browser.setDownloadBehavior', {
behavior: 'allow', downloadPath: DIR, eventsEnabled: true,
});
באותה מסגרת, נתיב הלכידה דרך CDP לא שמר על שם הקובץ המבוקש. הכותרת וכל שלוש השורות נשמרו בשלמותן, אבל probe-export.csv נשמר כ־download.csv. זו התנהגות שנצפתה בגרסאות ובקונפיגורציה שנבדקו, לא invariant מתועד לכל הורדה מתוך הרחבה. בדקו את התוכן בנפרד משם הקובץ.
הערת הרצה: התנהגות file:// עבור נתיב התקנה כזה
במהלך האימות של החלק הזה, טענה נפוצה מאוד התבררה כשגויה, והיא אפילו נכנסה לטיוטה מוקדמת של המאמר הזה: ש־content scripts לא רצים על עמודי file:// כי להרחבות אין גישה לקבצים כברירת מחדל.
במדידה על שתי גרסאות Chrome for Testing, טעינת ההרחבה משורת הפקודה וניווט ישיר אל file:///…/fixture/index.html: ה־content script מוזרק כרגיל. ה־marker נוכח, מזהה ההרחבה נכון, בשתי הגרסאות. הרחבות unpacked שנטענות משורת הפקודה מקבלות גישה לקבצים; מתג "grant file access" שאנשים זוכרים שייך לנתיב התקנה אחר.
עדיין עדיף להגיש fixtures דרך HTTP כברירת מחדל, כי דף file:// לא באמת דומה ליעד אמיתי. אבל זו טענה על ריאליזם, לא על הכרח טכני, והמנגנון שמייחסים לזה בדרך כלל פשוט לא נכון.
מה שהדבר הזה לא מוכיח
- המימוש של החסם נחשף רק עד רמת מחרוזת האזהרה. Chrome אומר שהדגלים האלה לא מותרים ב־build הזה ומציין את קובץ המקור. לא קראנו מתוך המקור אם זה נובע מקונפיגורציית build, מ־policy plumbing, או ממשהו אחר.
- זהו רק מחשב אחד. macOS על arm64, גרסת patch אחת של Chrome סטנדרטי, ושתי גרסאות Chrome for Testing. Chrome זז מהר מספיק כדי שצריך לאמת שוב ולא רק לצטט.
- זהו רק נתיב
--load-extension. לא נבדקו התקנות של.crxארוז, טעינה במצב developer, או policies של allowlist ארגוני. שום דבר כאן לא תומך בטענה ש"Chrome סטנדרטי לא יכול להריץ הרחבות". - ההרחבה היא stub שנבנה למטרה זו. היא בודקת את המכניקה שכל הרחבת scraper משתמשת בה, אבל הרחבה אמיתית גדולה יותר ויכולה להיכשל בדרכים שה־stub לא.
מה טעיתי בדרך לשם
שווה לומר את זה במפורש, כי שתי הטעויות הן בדיוק מהסוג ששורד review כשהתוצאה נראית נכונה.
הגרסה הראשונה של הכתיבה הזו אמרה שהכישלון היה שקט, שאין שורת לוג, ושהמנגנון לא ניתן לידיעה — שכל מי שטוען שהוא יודע בעצם מנחש. המנגנון היה דגל אחד משם, ו־Chrome הדפיס אותו ב־WARNING לכל אורך הדרך. "לא מצאתי את זה" נכתב כאילו הוא "אי אפשר למצוא את זה".
השנייה הייתה טענת file:// שלמעלה: נלקחה מהערות ונדבקה אליה פרשנות מנגנונית בטוחה מדי עוד לפני שנבדקה. ריצה אחת הפריכה אותה.
התבנית בשני המקרים זהה. טענה סבירה שאיש לא יאתגר, שמתקדמת הלאה כי הבדיקה מרגישה מיותרת. התיקון הוא לא יותר זהירות בניסוח, אלא כלל לגבי מה מותר לטעון: טענה שאי אפשר לעקוב אחריה לריצה לא נכנסת לפרסום.
הפקודות ששימשו בהרצה המקומית
ה־probe עם הגרסה שלו, ה־stub של ההרחבה, ה־fixture, והסיכומים הגולמיים מקושרים כאן, אבל זה עדיין לא bundle עצמאי לשחזור ציבורי. מקורות ההורדה וה־checksums של Chrome for Testing 149 ו־151 לא מתועדים במאמר, ונתיבי ה־executable למטה היו קלט מקומי. פרסמו את מקורות הדפדפנים האלה יחד עם commit יציב ברפוזיטורי לפני שמציגים את ההשוואה המלאה כשחזור עצמאי.
סקירה קשורה: סקירת Playwright.
cd harness
npm install playwright@1.56.0
npx playwright install chromium # Chromium של Playwright; לא שתי זרועות CfT למטה
(cd fixture && python3 -m http.server 8731 &)
SP=$(pwd) OUT=cft-149.json LABEL=cft-149 EXE="<Chrome for Testing 149>" node probe_v2.mjs
SP=$(pwd) OUT=stock-150.json LABEL=stock-150 CHANNEL=chrome node probe_v2.mjs
SP=$(pwd) OUT=cft-151.json LABEL=cft-151 EXE="<Chrome for Testing 151>" node probe_v2.mjs
אותו סקריפט ואותם ארגומנטים מפורשים של ההרחבה שימשו בשלוש הזרועות, אבל פתרון ה־executable היה שונה: CHANNEL=chrome עבור Chrome הסטנדרטי ו־EXE עבור שני הבינריים של Chrome for Testing. הגרסה בכל קובץ פלט נקראת מהדפדפן עצמו ולא נסמכת על התווית.
עבור שורת האזהרה לא צריך harness:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--user-data-dir=/tmp/p --enable-logging=stderr \
--load-extension=/path/to/ext about:blank 2>&1 | grep "not allowed"
נכון ל־2026-07-28.
נסה את Thunderbit לחילוץ נתוני רשת
מסקנה והסתייגויות
במערך שנבדק, Google Chrome 150 הסטנדרטי סירב ל־--disable-extensions-except יחד עם --load-extension, בעוד Chrome for Testing 149 ו־151 כן טענו את ההרחבה. Chrome רשם את הסירוב ברמת WARNING: "--load-extension is not allowed in Google Chrome, ignoring." הניסוח תומך בפרשנות של build ממותג, ו”הסנדוויץ’ של הגרסאות“ שולל הסרה מונוטונית פשוטה, אבל build וגרסה עדיין נשארים משולבים בלי זרוע cross-build באותה גרסה או בלי ראיות ממקור/קונפיגורציה.
למסגרת הרצת הרחבות מקובעת, השתמשו ב־browser executable עם גרסה מפורשת ותבדקו גם service worker וגם marker של ה־content script. בהגדרת Playwright 1.56.0 הזו, ייצוא ההרחבה דרש לכידת CDP של תיקייה והגיע תחת שם קובץ אחר. ה־stub שנטען משורת הפקודה הוזרק גם על ה־fixture file:// שנבדק; נתיבי התקנה אחרים לא נבדקו.
נסה את Thunderbit לחילוץ נתוני רשת Get Started Free
שאלות נפוצות
האם --load-extension הוסר מ־Chrome לגמרי?
לא. Chrome for Testing 149.0.7827.55 ו־151.0.7922.10 טענו שניהם את ה־MV3 unpacked stub מהדגל הזה והשלימו את שש הבדיקות המדורגות. Google Chrome 150.0.7871.187 הסטנדרטי סירב לכך ורשם "--load-extension is not allowed in Google Chrome, ignoring". זה תומך, אבל לא מוכיח, בחסם של build ממותג: ההתנהגות שוללת הסרה מונוטונית פשוטה, בעוד רגרסיה ספציפית ל־Chrome 150 שתוקנה ב־151 עדיין תואמת את העיצוב של שלוש זרועות.
למה אני לא רואה שום שגיאה, ואיך מבדילים בין "מעולם לא נטען" לבין "נטען ונכשל בשקט"?
האזהרה לא מודפסת ברמת verbosity ברירת מחדל. אם מפעילים עם --enable-logging=stderr היא מופיעה מיד; בלעדיו Chrome עולה כרגיל אבל שירות ההרחבות מתעלם מהדגלים, והתסמין הראשון במסגרת הבדיקה הוא timeout של selector. כדי להפריד בין “מעולם לא נטען” לבין כשל injection, השתמשו בשני אותות עצמאיים: service worker של MV3 ו־marker של content script בתוך ה־DOM של היעד. בזרוע של Chrome הסטנדרטי שנבדקה אף אחד מהם לא הופיע; בשתי זרועות Chrome for Testing כן.
במה כדאי להשתמש לאוטומציה של הרחבות במקום?
הזרועות שעברו השתמשו ב־Chrome for Testing מקובע דרך executablePath. Chromium שמנוהל על ידי Playwright הוא בינריים אפשרי נוסף לאוטומציה, אבל הוא לא היה זרוע במבחן הזה, ולא נכון לתאר אותו כאותה הפצת דפדפן בלי לבדוק את ה־executable שנפתר בפועל.
למה waitForEvent('download') לא מסתיים כשההרחבה מייצאת קובץ?
בהגדרת MV3/Playwright 1.56.0 הזו, האירוע לא הסתיים ולא הוצע שם קובץ. מסגרת הבדיקה פתחה session של CDP, קראה ל־Browser.setDownloadBehavior עם תיקייה מפורשת, ואז קראה את הקובץ מהדיסק. בריצות שנבדקו, בתים של ה־CSV נשמרו אבל probe-export.csv הגיע כ־download.csv; לא נבדקו שילובים רחבים יותר של הרחבות ודפדפנים.
מה הדבר הזה לא אומר — על כתובות file://, ועל Chrome הסטנדרטי בכלל?
שתי מגבלות, בכיוונים מנוגדים. Content scripts כן עובדים על כתובות file:// עבור הרחבות שנטענו משורת הפקודה — נבדק בשתי גרסאות Chrome for Testing, עם injection רגיל ודיווח נכון על מזהה ההרחבה, כך שהטענה הנפוצה שהם לא עובדים כי אין להרחבות גישה לקבצים כברירת מחדל היא שגויה עבור נתיב ההתקנה הזה. עדיין עדיף להגיש fixtures דרך HTTP כי זה דומה יותר ליעד אמיתי, לא כי file:// חוסם injection. בכיוון השני: שום דבר כאן לא אומר ש־Chrome סטנדרטי לא יכול להריץ הרחבות. נמדד רק נתיב שורת הפקודה של --load-extension. התקנת .crx ארוז, טעינה במצב developer ו־enterprise allowlist policies לא נבדקו, ולא נטענת לגביהם שום טענה. היקף הבדיקה הוא צמד הדגלים הזה בדיוק, זה שהמדריכים לאוטומציה אומרים להשתמש בו.


