Hai cờ lệnh này thường xuất hiện trong các ví dụ tự động hóa extension:
--disable-extensions-except=/path/to/ext --load-extension=/path/to/ext
Các công thức cũ thường trỏ Puppeteer hoặc Playwright vào một bản Chrome đã cài sẵn rồi truyền cả hai cờ này.
Trong bản Google Chrome 150 gốc được kiểm tra ở đây, trình duyệt vẫn khởi động bình thường, không báo lỗi trên dòng lệnh, nhưng dịch vụ extension lại bỏ qua cả hai cờ. Automation vẫn kết nối được, extension thì không xuất hiện; triệu chứng xuất hiện muộn hơn là harness bị timeout khi đợi selector.
Chrome có ghi lại lý do từ chối trong một dòng duy nhất, nhưng chỉ sau khi bật logging ra stderr.
Phát hiện chính ở đây là sự so sánh giữa các bản build trình duyệt. Hai phần phía sau là ghi chú về harness một cách rõ ràng: một phần nói về tải xuống do extension kích hoạt, phần còn lại nói về đường dẫn cài đặt qua dòng lệnh trên các fixture file://. Thunderbit bản thân là một Chrome extension, nên chúng tôi có liên quan trực tiếp đến vấn đề này; Thunderbit không phải đối tượng thử nghiệm.
Dòng thông báo đó
Thêm --enable-logging=stderr và khởi chạy Chrome gốc với các cờ này:
Tài liệu tham chiếu chính thức: mã nguồn extension service của Chromium.
WARNING:chrome/browser/extensions/extension_service.cc:442]
--disable-extensions-except is not allowed in Google Chrome, ignoring.
Nếu chỉ truyền riêng --load-extension, bạn sẽ thấy một cảnh báo khác, từ một dòng khác trong cùng file:
WARNING:chrome/browser/extensions/extension_service.cc:420]
--load-extension is not allowed in Google Chrome, ignoring.
Chrome for Testing, khi nhận đúng các cờ giống hệt, lại không in ra cảnh báo nào.
"Không được phép trong Google Chrome." Câu cảnh báo này khiến cách hiểu hợp lý nhất là đây là một quy tắc riêng của bản build có thương hiệu. Nó chứng minh rằng bản Google Chrome 150 gốc đã bỏ qua cờ lệnh và chỉ ra vị trí nguồn phát sinh. Nhưng nó không tự chứng minh rằng phiên bản đó không liên quan, cũng không cho biết yếu tố nào trong triển khai quyết định hành vi này.
Tất cả phần dưới đây là để xác nhận và phân tích hệ quả.
Xác nhận bằng hành vi thực tế
Một extension MV3 tối giản, được dựng riêng cho bài test chứ không tải từ nơi khác, dùng content script, popup, vòng gửi nhận message, trích xuất DOM và xuất chrome.downloads trên một fixture cục bộ gồm ba sản phẩm. Harness ghi lại sáu kiểm tra có đánh số: service worker đã đăng ký; marker của extension có ID; marker của content script khớp; nút trong popup hiển thị; popup trả về ba dòng; CSV được chụp lại chứa đúng hàng mong đợi. Một kiểm tra đối chiếu riêng so sánh hai tín hiệu độc lập từ service worker và page marker.
Ba bản build dùng cùng một bộ cờ extension rõ ràng, cùng thư mục extension, cùng fixture HTTP, cùng chế độ persistent context có giao diện và mỗi nhánh có profile mới. Nhánh stock được phân giải qua channel: 'chrome'; hai nhánh thành công dùng đường dẫn executable chỉ định rõ. Chuỗi phiên bản được đọc lại qua CDP.
| Build | Phiên bản do trình duyệt báo cáo | Service worker của extension | Content script được chèn | Toàn bộ 6 kiểm tra |
|---|---|---|---|---|
| Chrome for Testing | Chrome/149.0.7827.55 | ✅ | ✅ | ĐẠT 6/6 |
| Google Chrome gốc | Chrome/150.0.7871.187 | ❌ chưa từng đăng ký | ❌ | THẤT BẠI ở bước 0 |
| Chrome for Testing | Chrome/151.0.7922.10 | ✅ | ✅ | ĐẠT 6/6 |
Tóm tắt thô: Chrome for Testing 149, Chrome gốc 150, Chrome for Testing 151, và các cảnh báo stderr.
Chrome for Testing 149 được đưa vào có chủ ý: nó cũ hơn bản build stock mà nó được so sánh. Nếu khả năng này bị loại bỏ do nâng version, thì bản cũ hơn phải nằm ở phía hoạt động được, còn bản lỗi phải là bản mới nhất. Nhưng thực tế bản lỗi lại nằm giữa hai bản hoạt động tốt, nên không thể giải thích bằng một tiến trình tăng dần đơn giản theo phiên bản.
Bản thân bảng này vẫn chưa chứng minh được rào cản nằm ở build hay không, và cần nói thật chính xác vì sao. Ô lỗi duy nhất đồng thời cũng là ô stock duy nhất và ô 150 duy nhất — build và version vẫn bị trộn lẫn hoàn toàn trong thiết kế này. Ba nhánh chỉ loại trừ được việc bị gỡ bỏ theo kiểu đơn điệu; chúng không thể loại trừ giả thuyết “bị lỗi ở 150 rồi phục hồi ở 151”. Muốn chứng minh điều đó chỉ từ hành vi thì cần một ô mà máy này không thể tạo ra: Chrome for Testing 150, hoặc một bản build có thương hiệu ở phiên bản khác.
Cảnh báo này làm mạnh thêm cách hiểu rằng đây là quy tắc riêng của bản build có thương hiệu vì nó nêu đích danh Google Chrome, còn kết quả ba nhánh chỉ loại trừ việc gỡ bỏ đơn điệu đơn giản. Vẫn cần so sánh chéo giữa hai bản build cùng phiên bản hoặc trích dẫn từ source/cấu hình để chứng minh cơ chế không phụ thuộc version.
Các artifact theo từng bản build ở link cho thấy bản tóm tắt của từng nhánh; bài viết này không công bố ma trận chạy từng lần cho số lượt khởi chạy bổ sung, nên không dùng con số đó như bằng chứng độc lập.
Một tín hiệu thôi chưa đủ để nói rằng “nó không load”
Phiên bản đầu tiên của bài test quyết định extension đã load hay chưa bằng cách nhìn vào một marker mà content script ghi vào trang — và cũng chính marker đó được dùng để kết luận content script có chạy hay không. Một tín hiệu, dùng cho hai phép đo. Nếu marker mất, bạn không thể biết là “extension chưa bao giờ load” hay “nó đã load nhưng content script không được inject”, và hai trường hợp này cần cách sửa hoàn toàn khác nhau.
Extension MV3 chạy service worker nền, và Playwright có thể đọc trực tiếp service worker. Đây là một tín hiệu độc lập: nó không chạm vào trang, nên không thể bị nhầm với việc inject. Test hiện tại đọc cả hai và kiểm tra chúng có khớp nhau không.
Trong cả ba lần chạy, hai tín hiệu đều khớp. Trên Chrome gốc, service worker chưa từng được đăng ký — đây là dạng khẳng định mạnh nhất. Trên cả hai bản Chrome for Testing, worker xuất hiện tại chrome-extension://<id>/background.js trước cả khi trang được mở.
Hãy dùng Chrome for Testing, có thể bạn đã có sẵn
Hai nhánh thành công dùng các executable Chrome for Testing được ghim sẵn, báo cáo phiên bản 149.0.7827.55 và 151.0.7922.10. Hãy trỏ executablePath của Playwright vào executable đã ghim thay vì để channel: 'chrome' tự phân giải sang Chrome gốc. npx playwright install chromium sẽ cài một bản Chromium do Playwright quản lý; bản đó cũng hữu ích cho automation, nhưng nó không cùng nhãn phân phối với hai nhánh Chrome for Testing ở đây và không được tính như nhánh thứ tư trong so sánh này.
Tài liệu tham chiếu chính thức: thông báo về Chrome for Testing.
Bài review liên quan: kiểm tra quyền của Chrome extension scraper.
Có một lợi thế phụ kéo dài hơn cả lỗi cụ thể này. Chrome gốc tự cập nhật ngầm phía dưới bạn, nên bộ test hôm nay pass thì đến thứ Ba có thể fail mà không có commit nào giải thích được. Chrome for Testing thì được ghim version. Với bất kỳ thứ gì mà kết quả của nó vẫn còn ý nghĩa sau ba tháng, điều đó quan trọng hơn nhiều so với sự tiện tay.
Ghi chú harness: chụp file export của extension
Với một extension scraping, phần export là mục đích chính — nơi dễ mất field, méo encoding và làm phẳng sai dữ liệu lồng nhau. Có hai điều ở đây không được tài liệu hóa và cả hai đều gây lỗi.
| Kết quả được ghi lại trong lần export | Giá trị |
|---|---|
playwright_download_event_fired | false |
suggestedFilename | null |
| Tên file extension yêu cầu | probe-export.csv |
| Tên file thực sự rơi vào thư mục | download.csv |
| Nội dung file | tiêu đề và cả 3 dòng đều còn nguyên vẹn |
Trong harness MV3/Playwright 1.56.0 này, chrome.downloads không kích hoạt được download event của Playwright. Khi extension xuất CSV qua API, waitForEvent('download') không bao giờ resolve. Harness đã bắt file bằng cách mở một phiên CDP, thiết lập rõ hành vi tải xuống, rồi đọc thư mục đầu ra:
const cdp = await ctx.newCDPSession(page);
await cdp.send('Browser.setDownloadBehavior', {
behavior: 'allow', downloadPath: DIR, eventsEnabled: true,
});
Trong cùng harness đó, đường chụp bằng CDP lại không giữ nguyên tên file được yêu cầu. Tiêu đề và cả ba dòng vẫn nguyên vẹn, nhưng probe-export.csv lại rơi xuống thành download.csv. Đây là hành vi quan sát được với các bản build và cấu hình đã test, không phải một bất biến đã được tài liệu hóa cho mọi lượt tải extension. Hãy assert nội dung riêng với tên file.
Ghi chú harness: hành vi file:// cho đường cài đặt này
Khi xác minh phần này, một khẳng định phổ biến hóa ra là sai, và nó đã từng xuất hiện trong bản nháp trước của chính bài viết này: content script không chạy trên trang file:// vì extension mặc định không có quyền truy cập file.
Khi đo trên cả hai bản Chrome for Testing, load extension từ dòng lệnh rồi điều hướng thẳng tới file:///…/fixture/index.html: content script vẫn inject bình thường. Marker có mặt, extension id đúng, cả hai bản đều vậy. Unpacked extension được load qua dòng lệnh có quyền truy cập file; cái toggle “grant file access” mà nhiều người nhớ tới là dành cho một đường cài đặt khác.
Serve fixture qua HTTP vẫn là lựa chọn tốt hơn, vì file:// không giống một target thật. Nhưng đó là lý do về độ chân thực, không phải yêu cầu kỹ thuật, và cơ chế thường được nói cho lý do đó là sai.
Điều mà bài này chưa chứng minh
- Cơ chế của rào cản chỉ sâu đến mức của chuỗi cảnh báo. Chrome nói rằng các cờ này không được phép trong bản build này và nêu tên file nguồn. Nhưng nó được quyết định bởi build config, policy plumbing hay thứ gì khác thì chưa được bóc ra từ source.
- Đây chỉ là một máy. macOS trên arm64, một bản vá stock, hai bản Chrome for Testing. Chrome thay đổi đủ nhanh để kết luận này cần được kiểm tra lại thay vì chỉ viện dẫn.
- Đây chỉ là đường dẫn
--load-extension. Cài đặt.crxđóng gói, load ở chế độ developer, và policy allowlist của enterprise đều chưa được test. Không có gì ở đây hỗ trợ kết luận “Chrome gốc không chạy được extension”. - Extension trong test là stub dựng riêng cho mục đích này. Nó kiểm tra đúng các cơ chế mà một scraper extension thực sự dùng, nhưng extension thật thì lớn hơn và có thể lỗi theo những cách stub không thể mô phỏng.
Tôi đã sai ở đâu trong quá trình đi tới đây
Cần nói thẳng, vì cả hai lỗi đều thuộc kiểu rất dễ vượt qua review khi kết quả nhìn có vẻ đúng.
Phiên bản đầu tiên của bài viết này nói rằng lỗi là im lặng, không có dòng log nào, và cơ chế thì không thể biết — ai bảo biết là đang đoán. Nhưng thực tế cơ chế chỉ cách một cờ lệnh, và Chrome đã in nó ở mức WARNING suốt thời gian đó. “Tôi không tìm thấy” đã bị viết thành “không thể tìm thấy”.
Lỗi thứ hai là khẳng định về file:// ở trên: được nhắc lại từ ghi chú và gắn với một cơ chế nghe rất chắc chắn trước khi được kiểm tra. Một lần chạy đã bác bỏ nó.
Mẫu số chung trong cả hai trường hợp là giống nhau. Một câu nghe có vẻ hợp lý, không ai phản đối, rồi được giữ lại vì việc kiểm tra có vẻ không cần thiết. Cách sửa không phải là cẩn thận hơn trong câu chữ, mà là đặt ra quy tắc cho điều được phép nói: một khẳng định không truy vết được về một lần chạy thì không được xuất bản.
Các lệnh dùng trong harness cục bộ
Các bản probe, extension stub, fixture, và các tóm tắt thô đều được liên kết ở đây, nhưng đây vẫn chưa phải là một gói tái hiện công khai độc lập. Nguồn tải Chrome for Testing 149 và 151 cùng checksum chính xác chưa được ghi lại trong bài, và các đường dẫn executable bên dưới là input cục bộ. Hãy công bố nguồn trình duyệt đó cùng một commit ổn định của repo trước khi xem toàn bộ so sánh là có thể tái hiện độc lập.
Bài review liên quan: Playwright review.
cd harness
npm install playwright@1.56.0
npx playwright install chromium # Chromium do Playwright quản lý; không phải hai nhánh CfT bên dưới
(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
Cùng một script và cùng các đối số extension rõ ràng được dùng cho cả ba nhánh, nhưng cách phân giải executable khác nhau: CHANNEL=chrome cho Chrome gốc và EXE cho hai binary Chrome for Testing. Phiên bản trong từng file output được đọc từ browser chứ không dựa vào nhãn.
Với dòng cảnh báo, không cần 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"
Tính đến 2026-07-28.
Dùng thử Thunderbit để trích xuất dữ liệu web
Kết luận và lưu ý
Trong ma trận đã test, Google Chrome 150 gốc từ chối --disable-extensions-except cùng --load-extension, còn Chrome for Testing 149 và 151 thì load extension bình thường. Chrome ghi lý do từ chối ở mức WARNING: "--load-extension is not allowed in Google Chrome, ignoring." Cách diễn đạt này ủng hộ cách hiểu rằng đây là quy tắc của bản build có thương hiệu, và “sandwich” của phiên bản cho thấy đây không phải là việc gỡ bỏ đơn điệu đơn giản, nhưng build và version vẫn bị trộn lẫn nếu không có một nhánh cross-build cùng phiên bản hoặc bằng chứng từ source/cấu hình.
Với một harness extension đã ghim, hãy dùng browser executable được chỉ định phiên bản rõ ràng và kiểm tra cả service worker lẫn marker của content script. Trong setup Playwright 1.56.0 này, file export của extension cần được chụp qua CDP và đi ra với tên file khác. Stub được load bằng dòng lệnh cũng inject trên fixture file:// đã test; các đường cài đặt khác chưa được kiểm tra.
Dùng thử Thunderbit để trích xuất dữ liệu web Get Started Free
Câu hỏi thường gặp
--load-extension có bị loại bỏ hoàn toàn khỏi Chrome không?
Không. Chrome for Testing 149.0.7827.55 và 151.0.7922.10 đều load được MV3 unpacked stub bằng cờ này và hoàn thành đủ sáu kiểm tra. Google Chrome 150.0.7871.187 gốc từ chối và ghi "--load-extension is not allowed in Google Chrome, ignoring". Điều này ủng hộ, nhưng chưa chứng minh được, rằng đây là rào cản theo bản build có thương hiệu: hành vi loại trừ việc gỡ bỏ đơn điệu đơn giản, trong khi giả thuyết lỗi riêng của Chrome 150 rồi được sửa ở 151 vẫn phù hợp với thiết kế ba nhánh.
Vì sao tôi không thấy lỗi nào, và làm sao phân biệt “chưa bao giờ load” với “load rồi fail âm thầm”?
Cảnh báo không hiện ở mức log mặc định. Hãy khởi chạy với --enable-logging=stderr thì nó sẽ hiện ngay; nếu không, Chrome vẫn khởi động bình thường nhưng extension service sẽ bỏ qua các cờ, và triệu chứng đầu tiên của harness là timeout selector. Để tách bạch “chưa bao giờ load” với lỗi inject, hãy dùng hai tín hiệu độc lập: background service worker của MV3 và marker của content script trong DOM mục tiêu. Trên nhánh Chrome gốc đã test, cả hai đều không xuất hiện; trên cả hai nhánh Chrome for Testing, cả hai đều xuất hiện.
Vậy tôi nên dùng gì để tự động hóa extension thay thế?
Các nhánh thành công dùng executable Chrome for Testing đã ghim thông qua executablePath. Chromium do Playwright quản lý cũng là một binary automation khả dĩ, nhưng nó không phải là một nhánh trong test này và không nên được mô tả là cùng một distribution nếu chưa kiểm tra executable thực tế được phân giải ra.
Vì sao waitForEvent('download') không bao giờ resolve khi extension xuất file?
Trong setup MV3/Playwright 1.56.0 này, event không resolve và cũng không có suggested filename. Harness mở một phiên CDP, gọi Browser.setDownloadBehavior với thư mục đích rõ ràng, rồi đọc file từ đĩa. Trong các lần chạy đã test, bytes CSV vẫn nguyên, nhưng probe-export.csv lại thành download.csv; các tổ hợp extension và browser rộng hơn chưa được test.
Bài này không nói gì — về URL file://, và về Chrome gốc nói chung?
Có hai ranh giới, theo hai hướng ngược nhau. Content script có hoạt động trên URL file:// đối với extension được load từ dòng lệnh — đã test trên cả hai bản Chrome for Testing, script inject bình thường và extension id báo đúng, nên khẳng định phổ biến rằng chúng không hoạt động (vì extension mặc định không có quyền file access) là sai đối với đường cài đặt này. Serve fixture qua HTTP vẫn là thực hành tốt hơn vì nó giống target thật hơn, chứ không phải vì file:// chặn injection. Ở chiều ngược lại: không có gì ở đây nói rằng Chrome gốc không chạy extension. Chỉ đường dẫn cờ --load-extension được đo. Cài đặt .crx đóng gói, load ở chế độ developer và policy allowlist của enterprise thì chưa, và không có khẳng định nào được đưa ra về chúng. Phạm vi chỉ là cặp cờ mà các hướng dẫn automation thường bảo bạn dùng.


