uBlock Origin

extension name.

ਆਖਰਕਾਰ ਪ੍ਰਭਾਵੀ ਬਲੌਕਰ ਹੈ। CPU ਅਤੇ ਮੈਮੋਰੀ ਲਈ ਸੌਖਾ।

this will be in the Chrome web store: must be 132 characters or less

uBlock₀ — ਡੈਸ਼ਬੋਰਡ

English: uBlock₀ — Dashboard

ਸਾਵਧਾਨ! ਤੁਹਾਡੇ ਕੋਲ ਨਾ-ਸੰਭਾਲੀਆਂ ਤਬਦੀਲੀਆਂ ਹਨ

A warning in the dashboard when navigating away from unsaved changes

ਰਹੋ

Label for button to prevent navigating away from unsaved changes

ਅਣਡਿੱਠਾ

Label for button to ignore unsaved changes

ਸੈਟਿੰਗਾਂ

appears as tab name in dashboard

ਫਿਲਟਰ ਸੂਚੀਆਂ

appears as tab name in dashboard

ਮੇਰੇ ਫਿਲਟਰ

appears as tab name in dashboard

ਮੇਰੇ ਨਿਯਮ

appears as tab name in dashboard

ਭਰੋਸੇਯੋਗ ਸਾਈਟਾਂ

appears as tab name in dashboard

ਸ਼ਾਰਟਕੱਟ

appears as tab name in dashboard

uBlock₀ — ਲੌਗਰ

Title for the logger window

ਇਸ ਬਾਰੇ

appears as tab name in dashboard

ਸਹਿਯੋਗ

appears as tab name in dashboard

uBlock₀ — ਸੰਪੱਤੀ ਦਰਸ਼ਕ

Title for the asset viewer page

ਤਕਨੀਕੀ ਸੈਟਿੰਗਾਂ

Title for the advanced settings page

ਕਲਿੱਕ: ਇਸ ਸਾਈਟ ਲਈ uBlock₀ ਨੂੰ ਅਸਮਰੱਥ/ਸਮਰੱਥ ਕਰੋ।

Ctrl+ਕਲਿੱਕ: ਸਿਰਫ਼ ਇਸ ਸਫ਼ੇ ਲਈ uBlock₀ ਨੂੰ ਅਸਮਰੱਥ/ਸਮਰੱਥ ਕਰੋ।

English: Click: disable/enable uBlock₀ for this site.

Ctrl+click: disable uBlock₀ only on this page.

ਇਸ ਸਾਈਟ ਲਈ uBlock₀ ਨੂੰ ਅਸਮਰੱਥ/ਸਮਰੱਥ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ।

ਸਿਰਫ਼ ਇਸ ਸਫ਼ੇ ਲਈ uBlock₀ ਨੂੰ ਅਸਮਰੱਥ/ਸਮਰੱਥ ਕਰਨ ਲਈ Ctrl+ਕਲਿੱਕ ਕਰੋ।

Message to be read by screen readers

ਇਸ ਸਾਈਟ ਲਈ uBlock₀ ਸਮਰੱਥ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ।

Message to be read by screen readers

ਪਾਬੰਦੀ ਲਾਈਆਂ ਬੇਨਤੀਆਂ

English: requests blocked

ਇਸ ਸਫ਼ੇ ਉੱਤੇ

English: on this page

{{count}} ({{percent}}%)

Example: 15 (13%)

ਇੰਸਟਾਲ ਕਰਨ ਤੋਂ ਬਾਅਦ

English: since install

ਜਾਂ

English: or

ਇਸ ਸਫ਼ੇ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਏ

For the new mobile-friendly popup design

ਇੰਸਟਾਲ ਹੋਣ ਤੋਂ ਬਾਅਦ ਪਾਬੰਦੀ ਲਾਏ

ਕਨੈਕਟ ਹੋਈਆਂ ਡੋਮੇਨਾਂ

ਡੈਸ਼ਬੋਰਡ ਨੂੰ ਖੋਲ੍ਹੋ

English: Click to open the dashboard

Enter element zapper mode

Tooltip for the element-zapper icon in the popup panel

Enter element picker mode

English: Enter element picker mode

ਲੌਗਰ ਖੋਲ੍ਹੋ

Tooltip used for the logger icon in the panel

ਇਸ ਵੈੱਬਸਾਈਟ ਉੱਤੇ ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ

Tooltip used for the 'chat' icon in the panel

ਇਸ ਸਾਈਟ ਲਈ ਸਾਰੇ ਪੌਪ-ਅੱਪ ਉੱਤੇ ਪਾਬੰਦੀਆਂ ਨੂੰ ਬਦਲੋ

Tooltip for the no-popups per-site switch

ਇਸ ਸਾਈਟ ਲਈ ਸਾਰੇ ਪੌਪ-ਅੱਪ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਸਾਰੇ ਪੌਪ-ਅੱਪ ਉੱਤੇ ਪਾਬੰਦੀ ਨਾ ਲਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਵੱਡੇ ਮੀਡਿਆ ਭਾਗਾਂ ਉੱਤੇ ਪਾਬੰਦੀਆਂ ਨੂੰ ਬਦਲੋ

Tooltip for the no-large-media per-site switch

ਇਸ ਸਾਈਟ ਲਈ ਵੱਡੇ ਮੀਡੀਆ ਭਾਗਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਵੱਡੇ ਮੀਡੀਆ ਭਾਗਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਹਟਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਦਿੱਖ ਫਿਲਟਰ ਕਰਨ ਨੂੰ ਬਦਲੋ

Tooltip for the no-cosmetic-filtering per-site switch

ਇਸ ਸਾਈਟ ਉੱਤੇ ਦਿੱਖ ਫਿਲਟਰ ਕਰਨ ਨੂੰ ਅਸਮਰੱਥ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਉੱਤੇ ਦਿੱਖ ਫਿਲਟਰ ਕਰਨ ਨੂੰ ਸਮਰੱਥ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਰਿਮੋਟ ਫ਼ੌਂਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀਆਂ ਨੂੰ ਬਦਲੋ

Tooltip for the no-remote-fonts per-site switch

ਇਸ ਸਾਈਟ ਲਈ ਰਿਮੋਟ ਫ਼ੌਂਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਲਈ ਰਿਮੋਟ ਫ਼ੌਂਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਹਟਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਇਸ ਸਾਈਟ ਉੱਤੇ JavaScript ਅਸਮਰੱਥ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ

Tooltip for the no-scripting per-site switch

ਇਸ ਸਾਈਟ ਉੱਤੇ JavaScript ਅਸਮਰੱਥ ਕਰਨ ਨੂੰ ਹਟਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ

ਪੌਪ-ਅੱਪ ਵਿੰਡੋ

Caption for the no-popups per-site switch

ਵੱਡੇ ਮੀਡੀਆ ਭਾਗ

Caption for the no-large-media per-site switch

ਦਿੱਖ ਫਿਲਟਰ ਕਰਨਾ

Caption for the no-cosmetic-filtering per-site switch

ਰਿਮੋਟ ਫ਼ੌਂਟ

Caption for the no-remote-fonts per-site switch

JavaScript

Caption for the no-scripting per-site switch

ਹੋਰ

Label to be used to show popup panel sections

ਘੱਟ

Label to be used to hide popup panel sections

ਗਲੋਬਲ ਨਿਯਮ: ਇਹ ਕਾਲਮ ਸਾਰੀਆਂ ਸਾਈਟਾਂ ਉੱਤੇ ਲਾਗੂ ਹੋਣ ਵਾਲੇ ਨਿਯਮਾਂ ਲਈ ਹੈ।

Tooltip when hovering the top-most cell of the global-rules column.

ਲੋਕਲ ਨਿਯਮ: ਇਹ ਕਾਲਮ ਨਿਯਮਾਂ ਲਈ ਹੈ, ਜੋ ਕਿ ਸਿਰਫ਼ ਲੋਕਲ ਸਾਈਟਾਂ ਉੱਤੇ ਹੀ ਲਾਗੂ ਹੁੰਦੇ ਹਨ।
ਲੋਕਲ ਨਿਯਮ ਗਲੋਬਲ ਨਿਯਮਾਂ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਦੇ ਹਨ।

Tooltip when hovering the top-most cell of the local-rules column.

ਆਪਣੀਆਂ ਤਬਦੀਲੀਆਂ ਨੂੰ ਪੱਕਾ ਬਣਾਉਣ ਲਈ ਕਲਿੱਕ ਕਰੋ।

Tooltip when hovering over the padlock in the dynamic filtering pane.

ਆਪਣੀਆਂ ਤਬਦੀਲੀਆਂ ਨੂੰ ਵਾਪਸ ਲੈਣ ਲਈ ਕਲਿੱਕ ਕਰੋ।

Tooltip when hovering over the eraser in the dynamic filtering pane.

ਸਭ

ਚਿੱਤਰ

ਤੀਜੀ-ਧਿਰ

ਤੀਜੀ-ਧਿਰ CSS/ਚਿੱਤਰ

ਇਨ-ਲਾਈਨ ਸਕ੍ਰਿਪਟਾਂ

1ਲੀ-ਧਿਰ ਸਕ੍ਰਿਪਟਾਂ

ਤੀਜੀ-ਧਿਰ ਸਕ੍ਰਿਪਟਾਂ

ਤੀਜੀ-ਧਿਰ ਫਰੇਮ

ਕਨੈਕਟ ਹੋਈਆਂ ਡੋਮੇਨਾਂ

appears in popup

{{total}} ਵਿੱਚੋਂ {{count}}

appears in popup

ਵਰਜ਼ਨ

Example of use: Version 1.26.4

ਸਕ੍ਰਿਪਟ

Appears as an option to filter out firewall rows

ਫਰੇਮ

ਬਣਾਓ

English: Create

ਚੁਣੋ

English: Pick

ਬਾਹਰ

English: Quit

ਝਲਕ

Element picker preview mode: will cause the elements matching the current filter to be removed from the page

ਨੈੱਟਵਰਕ ਫਿਲਟਰ

English: header for a type of filter in the element picker dialog

ਦਿੱਖ ਫਿਲਟਰ ਕਰਨਾ

English: Cosmetic filters

ਕਲਿੱਕ, Ctrl-ਕਲਿੱਕ

English: Click, Ctrl-click

ਭਾਗ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਓ…

An entry in the browser's contextual menu

ਪਾਬੰਦੀ ਲਾਏ ਭਾਗਾਂ ਲਈ ਸਥਾਨ ਨੂੰ ਓਹਲੇ ਕਰੋ

English: Hide placeholders of blocked elements

ਆਈਕਾਨ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਈਆਂ ਬੇਨਤੀਆਂ ਦੀ ਗਿਣਤੀ ਦਿਖਾਓ

English: Show the number of blocked requests on the icon

ਟੂਲ-ਟਿੱਪ ਅਸਮਰੱਥ ਕਰੋ

A checkbox in the Settings pane

ਜਦੋਂ ਵੀ ਵਾਜਬ ਹੋਵੇ ਤਾਂ ਪ੍ਰਸੰਗ ਮੇਨੂ ਨੂੰ ਵਰਤੋਂ

English: Make use of context menu where appropriate

ਰੰਗ-ਅੰਨ੍ਹੇਪਣ ਲਈ ਸਰਲ

English: Color-blind friendly

ਦਿੱਖ

Section for controlling user interface appearance

ਥੀਮ

Label for checkbox to enable a custom dark theme

Custom accent color

Label for checkbox to pick an accent color

ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਸਹਾਇਤਾ ਸਮਰੱਥ ਕਰੋ

ਮੈਂ ਮਾਹਰ ਵਰਤੋਂਕਾਰ ਹਾਂ

Checkbox to let user access advanced, technical features

ਪਹਿਲਾਂ-ਪ੍ਰਾਪਤ ਕਰਨਾ ਅਸਮਰੱਥ (ਪਾਬੰਦੀ ਲਾਈਆਂ ਨੈੱਟਵਰਕ ਬੇਨਤੀਆਂ ਲਈ ਕਿਸੇ ਵੀ ਕਨੈਕਸ਼ਨ ਨੂੰ ਰੋਕਣ ਲਈ)

English:

ਹਾਈਪਰ-ਲਿੰਕ ਪੜਤਾਲ ਨੂੰ ਅਸਮਰੱਥ ਕਰੋ

English:

WebRTC ਨੂੰ ਲੋਕਲ IP ਐਡਰੈਸ ਲੀਕ ਕਰਨ ਤੋਂ ਰੋਕੋ

English:

ਮੂਲ ਰਵੱਈਆ

ਇਹ ਮੂਲ ਰਵੱਈਏ ਨੂੰ ਹਰ-ਸਟਾਈ ਮੁਤਾਬਕ ਅਣਡਿੱਠਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ

ਦਿੱਖ ਫਿਲਟਰ ਕਰਨ ਨੂੰ ਅਸਮਰੱਥ ਕਰੋ

{{input}} KB ਤੋਂ ਵੱਡੇ ਮੀਡੀਆ ਭਾਗਾਂ ਉੱਤੇ ਪਾਬੰਦੀ

ਰਿਮੋਟ ਫ਼ੌਂਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਓ

JavaScript ਅਸਮਰੱਥ ਕਰੋ

The default state for the per-site no-scripting switch

CSP ਰਿਪੋਰਟਾਂ ਉੱਤੇ ਪਾਬੰਦੀ

background information: https://github.com/gorhill/uBlock/issues/3150

Uncloak canonical names

background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513

ਤਕਨੀਕੀ

Section for controlling advanced-user settings

ਫ਼ੀਚਰ ਸਿਰਫ਼ ਤਕਨੀਕੀ ਮਾਹਰ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਢੁੱਕਵੇਂ ਹਨ

Description of section controlling advanced-user settings

ਤਕਨੀਕੀ ਸੈਟਿੰਗਾਂ

For the tooltip of a link which gives access to advanced settings

ਪਿਛਲਾ ਰੀ-ਸਟੋਰ:

English: Last restore:

ਪਿਛਲਾ ਬੈਕਅੱਪ:

English: Last backup:

{{netFilterCount}} ਨੈੱਟਵਰਕ ਫਿਲਟਰ ＋ {{cosmeticFilterCount}} ਦਿੱਖ ਫਿਲਟਰ, ਇੱਥੋਂ:

Appears at the top of the _3rd-party filters_ pane

{{total}} ਵਿੱਚੋਂ {{used}} ਵਰਤੇ

Appears aside each filter list in the _3rd-party filters_ pane

ਫਿਲਟਰ ਸੂਚੀਆਂ ਆਪੇ ਅੱਪਡੇਟ ਕਰੋ

A checkbox in the _3rd-party filters_ pane

ਹੁਣੇ ਅੱਪਡੇਟ ਕਰੋ

A button in the in the _3rd-party filters_ pane

ਸਭ ਕੈਸ਼ ਮਿਟਾਓ

ਦਿੱਖ ਫਿਲਟਰ ਨੂੰ ਪਾਰਸ ਅਤੇ ਲਾਗੂ ਕਰੋ

English: Parse and enforce Adblock+ element hiding filters.

ਦਿੱਖ (cosmetic) ਫਿਲਟਰ ਵੈੱਬ ਸਫ਼ੇ ਉੱਤੇ ਅਜਿਹਾ ਭਾਗਾਂ ਨੂੰ ਲੁਕਾਉਂਦੇ ਹਨ, ਜੋ ਕਿ ਦਿੱਖ ਵਿੱਚ ਰੁਕਾਵਟ ਬਣਦੇ ਹਨ ਅਤੇ ਜਿਹਨਾਂ ਉੱਤੇ ਨੈੱਟਵਰਕ ਅਧਾਰਿਤ ਫਿਲਟਰ ਇੰਜਣਾਂ ਰਾਹੀਂ ਪਾਬੰਦੀ ਨਹੀਂ ਲਾਈ ਜਾ ਸਕਦੀ ਹੈ।

Describes the purpose of the 'Parse and enforce cosmetic filters' feature.

ਆਮ ਦਿੱਖ ਆਧਾਰਿਤ ਫਿਲਟਰ ਅਣਡਿੱਠੇ ਕਰੋ

This will cause uBO to ignore all generic cosmetic filters.

Generic cosmetic filters are those cosmetic filters which are meant to apply on all web sites. Enabling this option will eliminate the memory and CPU overhead added to web pages as a result of handling generic cosmetic filters.

It is recommended to enable this option on less powerful devices.

Describes the purpose of the 'Ignore generic cosmetic filters' feature.

ਸਭ ਫਿਲਟਰ ਸੂਚੀਆਂ ਲੋਡ ਹੋਣ ਤੱਕ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਨੂੰ ਸਸਪੈਂਡ ਰੱਖੋ

A checkbox in the 'Filter lists' pane

Lists of blocked hosts

English: Lists of blocked hosts

ਤਬਦੀਲੀਆਂ ਲਾਗੂ ਕਰੋ

English: Apply changes

ਬਿਲਟ-ਇਨ

Filter lists section name

ਇਸ਼ਤਿਹਾਰ

Filter lists section name

ਪਰਦੇਦਾਰੀ

Filter lists section name

ਮਾਲਵੇਅਰਾਂ ਤੋਂ ਬਚਾਅ, ਸੁਰੱਖਿਆ

Filter lists section name

ਅਣਚਾਹੇ

Filter lists section name

ਬਹੁ-ਮਕਸਦੀ

Filter lists section name

ਖੇਤਰ, ਭਾਸ਼ਾਵਾਂ

Filter lists section name

ਕਸਟਮ

Filter lists section name

ਇੰਪੋਰਟ…

The label for the checkbox used to import external filter lists

ਹਰ ਲਾਈਨ ਵਿੱਚ ਇੱਕ URL। ਗਲਤ URL ਨੂੰ ਬਿਨਾਂ ਰੁਕੇ ਅਣਡਿੱਠਾ ਕੀਤਾ ਜਾਵੇਗਾ।

Short information about how to use the textarea to import external filter lists by URL

ਪੁਰਾਣਾ।

used as a tooltip for the out-of-date icon beside a list

ਸਮੱਗਰੀ ਵੇਖੋ

used as a tooltip for eye icon beside a list

ਆਖਰੀ ਅੱਪਡੇਟ: {{ago}}।
ਅੱਪਡੇਟ ਲਈ ਮਜ਼ਬੂਰ ਕਰਨ ਵਾਸਤੇ ਕਲਿੱਕ ਕਰੋ।

used as a tooltip for the clock icon beside a list

…ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ

used as a tooltip for the spinner icon beside a list

ਨੈੱਟਵਰਕ ਗਲਤੀ ਸਰੋਤ ਨੂੰ ਅੱਪਡੇਟ ਹੋਣ ਤੋਂ ਰੋਕਦੀ ਹੈ।

used as a tooltip for error icon beside a list

One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with <code>!</code> will be ignored.

Short information about how to create custom filters

ਗ਼ੈਰ-ਭਰੋਸੇਯੋਗ ਸਰੋਤਾਂ ਤੋਂ ਫਿਲਟਰ ਨਾ ਜੋੜੋ।

Warning against copy-pasting filters from random sources

ਇੰਪੋਰਟ ਕਰੋ ਤੇ ਜੋੜੋ…

Button in the 'My filters' pane

ਐਕਸਪੋਰਟ…

Button in the 'My filters' pane

my-ublock-static-filters_{{datetime}}.txt

English: my-ublock-static-filters_{{datetime}}.txt

ਤਬਦੀਲੀਆਂ ਲਾਗੂ ਕਰੋ

English: Apply changes

ਪੱਕੇ ਨਿਯਮ

header

ਆਰਜ਼ੀ ਨਿਯਮ

header

ਵਾਪਸ ਲਵੋ

This will remove all temporary rules

ਕਮਿਟ

This will persist temporary rules

ਸੋਧੋ

Will enable manual-edit mode (textarea)

ਸੰਭਾਲੋ

Will save manually-edited content and exit manual-edit mode

ਖ਼ਾਰਜ

Will discard manually-edited content and exit manual-edit mode

…ਫ਼ਾਈਲ ਤੋਂ ਇੰਪੋਰਟ ਕਰੋ

…ਫ਼ਾਈਲ ਉੱਤੇ ਐਕਸਪੋਰਟ ਕਰੋ

Button in the 'My rules' pane

my-ublock-dynamic-rules_{{datetime}}.txt

default file name to use

ਤੁਹਾਡੇ ਚਾਲੂ ਫਿਲਟਰ ਕਰਨ ਦੇ ਨਿਯਮਾਂ ਦੀ ਸੂਚੀ ਹੈ।

English: List of your dynamic filtering rules.

Rule syntax: source destination type action (full documentation).

English: dynamic rule syntax and full documentation.

ਲੜੀਬੱਧ:

English: label for sort option.

ਨਿਯਮ ਦੀ ਕਿਸਮ

English: a sort option for list of rules.

ਸਰੋਤ

ਟਿਕਾਣਾ

The trusted site directives dictate on which web pages uBlock Origin should be disabled. One entry per line.

A concise description of the 'Trusted sites' pane.

ਇੰਪੋਰਟ ਕਰੋ ਤੇ ਜੋੜੋ…

Button in the 'Trusted sites' pane

ਐਕਸਪੋਰਟ…

Button in the 'Trusted sites' pane

my-ublock-trusted-sites_{{datetime}}.txt

The default filename to use for import/export purpose

ਤਬਦੀਲੀਆਂ ਲਾਗੂ ਕਰੋ

English: Apply changes

ਕਿਸਮ

English: Type

ਡੋਮੇਨ

English: Domain

URL

English: URL

ਫਿਲਟਰ

English: Filter

ਸਭ

Appears in the logger's tab selector

ਬਿਨ-ਟੈਬ

Pretty name for behind-the-scene network requests

ਮੌਜੂਦਾ ਟੈਬ

Appears in the logger's tab selector

ਟੈਬ ਸਮੱਗਰੀ ਮੁੜ-ਲੋਡ ਕਰੋ

Tooltip for the reload button in the logger page

DOM ਇੰਸਪੈਕਟਰ ਨੂੰ ਬਦਲੋ

Tooltip for the DOM inspector button in the logger page

ਪੌਪ-ਅੱਪ ਪੈਨਲ ਬਦਲੋ

Tooltip for the popup panel button in the logger page

uBlock Origin ਵਿਕੀ: ਲੌਗਰ

Tooltip for the top-right info label in the logger page

ਲੌਗਰ ਮਿਟਾਓ

Tooltip for the eraser in the logger page; used to blank the content of the logger

ਲੌਗਰ ਨੂੰ ਰੋਕੋ (ਸਭ ਆਉਣ ਵਾਲੇ ਡਾਟੇ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰੋ)

Tooltip for the pause button in the logger page

ਲੌਗਰ ਨੂੰ ਚਾਲੂ ਕਰੋ

Tooltip for the play button in the logger page

ਲੌਗਰ ਫਿਲਟਰਾਂ ਨੂੰ ਬਦਲੋ

Tooltip for the row filterer button in the logger page

ਲੌਗਰ ਸਮੱਗਰੀ ਨੂੰ ਫਿਲਟਰ ਕਰੋ

Placeholder string for logger output filtering input field

ਲੌਗਰ ਫਿਲਟਰ ਕਰਨ ਲਈ ਚੋਣਾਂ

Tooltip for the button to bring up logger output filtering options

ਨਹੀਂ

A keyword in the built-in row filtering expression

eventful

A keyword in the built-in row filtering expression: all items corresponding to uBO doing something (blocked, allowed, redirected, etc.)

ਪਾਬੰਦੀ ਲਾਏ

ਮਨਜ਼ੂਰ ਕੀਤੇ

ਸੋਧ ਕੀਤੇ

1ਲੀ-ਧਿਰ

3ਜੀ-ਧਿਰ

ਵੇਰਵੇ

Small header to identify the 'Details' pane for a specific logger entry

ਫਿਲਟਰ

Label to identify a filter field

ਫਿਲਟਰ ਸੂਚੀ

Label to identify a filter list field

ਨਿਯਮ

Label to identify a rule field

ਪਰਸੰਗ

Label to identify a context field (typically a hostname)

ਰੂਟ ਪਰਸੰਗ

Label to identify a root context field (typically a hostname)

ਬਿਨ-ਧਿਰ

Label to identify a field providing partyness information

ਕਿਸਮ

Label to identify the type of an entry

URL

Label to identify the URL of an entry

URL ਨਿਯਮ

Small header to identify the dynamic URL filtering section

ਪਰਸੰਗ:

Label for the context selector

ਕਿਸਮ:

Label for the type selector

ਟਿਕਾਊ ਫਿਲਟਰ

Small header to identify the static filtering section

{{action}} network requests of {{type}} {{br}}which URL address matches {{url}} {{br}}and which originates {{origin}},{{br}}{{importance}} there is a matching exception filter.

Used in the static filtering wizard

ਪਾਬੰਦੀ

Used in the static filtering wizard

ਮਨਜ਼ੂਰ

Used in the static filtering wizard

ਕਿਸਮ “{{type}}”

Used in the static filtering wizard

ਕੋਈ ਵੀ ਕਿਸਮ

Used in the static filtering wizard

“{{origin}}” ਤੋਂ

Used in the static filtering wizard

ਕਿਸੇ ਵੀ ਥਾਂ ਤੋਂ

Used in the static filtering wizard

ਛੋਟ ਜਦੋਂ

Used in the static filtering wizard

ਭਾਵੇਂ ਜੇ

Used in the static filtering wizard

ਸਥਿਰ ਫਿਲਟਰ <code>{{filter}}</code> ਇਸ ਵਿੱਚ ਮੌਜੂਦ ਹੈ:

Below this sentence, the filter list(s) in which the filter was found

Static filter could not be found in any of the currently enabled filter lists

Message to show when a filter cannot be found in any filter lists

Logger entries which do not fulfill all three conditions below will be automatically discarded:

Logger setting: A sentence to describe the purpose of the settings below

ਪਿਛਲੇ {{input}} ਮਿੰਟਾਂ ਤੋਂ ਐਂਟਰੀਆਂ ਰੱਖੋ

A logger setting

Preserve at most {{input}} page loads per tab

A logger setting

Preserve at most {{input}} entries per tab

A logger setting

Use {{input}} lines per entry in vertically expanded mode

A logger setting

ਕਾਲਮ ਲੁਕਾਓ:

Logger settings: a sentence to describe the purpose of the checkboxes below

{{input}} ਸਮਾਂ

A label for the time column

{{input}} ਫਿਲਟਰ/ਨਿਯਮ

A label for the filter or rule column

{{input}} ਪਰਸੰਗ

A label for the context column

{{input}} ਬਿਨ-ਧਿਰ

A label for the partyness column

ਸੂਚੀ

Label for radio-button to pick export format

ਸਾਰਣੀ

ਸਰਲ

Label for radio-button to pick export text format

ਮਾਰਕਡਾਊਨ

ਖੋਲ੍ਹੋ

Text for button which open an external webpage in Support pane

ਨਵੀਂ ਰਿਪੋਰਟ ਬਣਾਓ

ਰਲਦੀਆਂ ਰਿਪੋਰਟਾਂ ਲੱਭੋ

A clickable link in the filter issue reporter section

ਦਸਤਾਵੇਜ਼

Header of 'Documentation' section in Support pane

uBlock Origin ਦੇ ਸਾਰੇ ਫੀਚਰਾਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਲੈਣ ਲਈ uBlock/wiki ਤੋਂ ਦਸਤਾਵੇਜ਼ ਪੜ੍ਹੋ।

First paragraph of 'Documentation' section in Support pane

ਸਵਾਲ ਅਤੇ ਸਹਿਯੋਗ

Header of 'Questions and support' section in Support pane

Answers to questions and other kinds of help support is provided on the subreddit <code>/r/uBlockOrigin</code>.

First paragraph of 'Questions and support' section in Support pane

ਫਿਲਟਰ ਮਸਲੇ/ਵੈੱਬਸਾਈਟ ਖਰਾਬ ਹੈ

Header of 'Filter issues' section in Support pane

Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.

First paragraph of 'Filter issues' section in Support pane

<b>Important:</b> Avoid using other similarly-purposed blockers along with uBlock Origin, as this may cause filter issues on specific websites.

Second paragraph of 'Filter issues' section in Support pane

Tips: Be sure your filter lists are up to date. The logger is the primary tool to diagnose filter-related issues.

Third paragraph of 'Filter issues' section in Support pane

ਬੱਗ ਰਿਪੋਰਟ

Header of 'Bug report' section in Support pane

Report issues with uBlock Origin itself to the uBlockOrigin/uBlock-issue issue tracker. Requires a GitHub account.

First paragraph of 'Bug report' section in Support pane

ਸਮੱਸਿਆ ਹੱਲ ਕਰਨ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ

Header of 'Troubleshooting Information' section in Support pane

ਹੇਠਾਂ ਤਕਨੀਕੀ ਜਾਣਕਾਰੀ ਦਿੱਤੀ ਹੈ, ਜੋ ਕਿ ਵਲੰਟੀਅਰਾਂ ਲਈ ਮਦਦਗਾਰ ਹੋ ਸਕਦੀ ਹੈ, ਜੋ ਤੁਹਾਡੀ ਸਮੱਸਿਆ ਹੱਲ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ ਕਰਦੇ ਹਨ।

First paragraph of 'Troubleshooting Information' section in Support pane

<b>ਖਾਸ:</b> ਸੰਭਾਵਿਤ ਤੌਰ ਉੱਤੇ ਪ੍ਰਾਈਵੇਟ ਜਾਂ ਸੰਵੇਦਨਸ਼ੀਲ ਜਾਣਕਾਰੀ ਨੂੰ ਮੂਲ ਰੂਪ ਵਿੱਚ ਛਾਂਟ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਛਾਂਂਟੀ ਕੀਤੀ ਜਾਣਕਾਰੀ ਸਮੱਸਿਆ ਨੂੰ ਹੱਲ ਕਰਨਾ ਵੱਧ ਔਖਾ ਬਣਾ ਸਕਦੀ ਹੈ।

Second paragraph of 'Troubleshooting Information' section in Support pane

ਫਿਲਟਰ ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ

Header of 'Report a filter issue' section in Support pane

ਵਲੰਟੀਅਰਾਂ ਕੋਲ ਡੁਪਲੀਕੇਟ ਰਿਪੋਰਟਾਂ ਦਾ ਕੰਮ ਵਧਾਉਣ ਤੋਂ ਬਚਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਮਸਲਾ ਕਿਤੇ ਪਹਿਲਾਂ ਰਿਪੋਰਟ ਤਾਂ ਨਹੀ ਕੀਤਾ ਹੋਇਆ ਹੈ।

A paragraph in the filter issue reporter section

ਫਿਲਟਰ ਸੂਚੀਆਂ ਰੋਜ਼ ਅੱਪਡੇਟ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਕਿਤੇ ਤੁਹਾਡਾ ਮਸਲਾ ਪਹਿਲਾਂ ਹੀ ਸਭ ਤੋਂ ਤਾਜ਼ਾ ਫਿਲਟਰ ਸੂਚੀਆਂ ਰਾਹੀਂ ਹੱਲ ਤਾਂ ਨਹੀਂ ਹੋ ਚੁੱਕਿਆ ਹੈ।

ਸਮੱਸਿਆ ਵਾਲੇ ਵੈੱਬ-ਸਫ਼ੇ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰਨ ਦੇ ਬਾਅਦ ਜਾਂਚ ਕਰੋ ਕਿ ਕੀ ਮਸਲਾ ਹਾਲੇ ਵੀ ਮੌਜੂਦ ਹੈ।

ਵੈੱਬ ਸਾਈਟ ਦਾ ਸਿਰਨਾਵਾਂ:

Label for the URL of the page

ਵੈੱਬ ਸਫ਼ਾ…

Label for widget to select type of issue

-- ਐਂਟਰੀ ਚੁਣੋ --

An entry in the widget used to select the type of issue

ਇਸ਼ਤਿਹਾਰ ਜਾਂ ਇਸ਼ਤਿਹਾਰ ਦੀ ਰਹਿੰਦ-ਖੂੰਦ ਦਿਖਾਉਂਦਾ ਹੈ

Has overlays or other nuisances

uBlock Origin ਖੋਜਦਾ ਹੈ

ਪਰਦੇਦਾਰੀ ਸੰਬੰਧੀ ਮਸਲੇ ਹਨ

uBlock Origin ਸਮਰੱਥ ਕਰਨ ਦੇ ਬਾਅਦ ਗੜਬੜ ਹੁੰਦੀ ਹੈ

ਬੇਲੋੜੀਆਂ ਟੈਬਾਂ ਜਾਂ ਵਿੰਡੋ ਖੋਲ੍ਹਦਾ ਹੈ

Label the web page as “NSFW” (“Not Safe For Work”)

A checkbox to use for NSFW sites

Redact

Text for 'Redact' button

Unredact

Text for 'Unredact' button

ਪਰਦੇਦਾਰੀ ਨੀਤੀ

Link to privacy policy on GitHub (English)

ਤਬਦੀਲੀ-ਸੂਚੀ

ਸਰੋਤ ਕੋਡ (GPLv3)

English: Source code (GPLv3)

ਯੋਗਦਾਨੀ

English: Contributors

ਸਰੋਤ ਕੋਡ

Link text to source code repo

ਅਨੁਵਾਦ

Link text to translations repo

ਫਿਲਟਰ ਸੂਚੀਆਂ

Link text to uBO's own filter lists repo

ਬਾਹਰੀ ਨਿਰਭਰਤਾਵਾਂ (GPLv3-ਅਨੁਕੂਲ):

Shown in the About pane

uBO's own filter lists are freely hosted on the following CDNs:

Shown in the About pane

A randomly picked CDN is used when a filter list needs to be updated.

Shown in the About pane

ਫ਼ਾਈਲ ਤੋਂ ਬੈਕਅੱਪ ਲਵੋ…

Text for button to create a backup of all settings

English: my-ublock-backup_{{datetime}}.txt

…ਫ਼ਾਈਲ ਤੋਂ ਬਹਾਲ ਕਰੋ

English: Restore from file...

…ਮੂਲ ਸੈਟਿੰਗਾਂ ਬਹਾਲ ਕਰੋ

English: Reset to default settings...

All your settings will be overwritten using data backed up on {{time}}, and uBlock₀ will restart.

Overwrite all existing settings using backed up data?

Message asking user to confirm restore

ਡਾਟਾ ਪੜ੍ਹਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ ਜਾਂ ਗ਼ੈਰ-ਵਾਜਬ ਹੈ

Message to display when an error occurred during restore

ਤੁਹਾਡੀਆਂ ਸਾਰੀਆਂ ਸੈਟਿੰਗਾਂ ਨੂੰ ਹਟਾਇਆ ਜਾਵੇਗਾ ਅਤੇ uBlock₀ ਮੁੜ-ਚਾਲੂ ਹੋਵੇਗਾ।

ਕੀ uBlock₀ ਨੂੰ ਫੈਕਟਰੀ ਸੈਟਿੰਗਾਂ ਲਈ ਮੁੜ-ਸੈੱਟ ਕਰਨਾ ਹੈ?

Message asking user to confirm reset

ਨੈੱਟਵਰਕ ਗਲਤੀ: {{msg}}

English: Network error: {{msg}}

ਕੀ ਅੱਗੇ ਦਿੱਤੇ URL ਨੂੰ ਤੁਹਾਡੀਆਂ ਕਸਟਮ ਫਿਲਟਰ ਸੂਚੀਆਂ ਵਿੱਚ ਜੋੜਨਾ ਹੈ?

ਨਾਂ: "{{title}}"
URL: {{url}}

No longer used

ਮੈਂਬਰ ਬਣੋ

For the button used to subscribe to a filter list

ਮਿੰਟ ਪਹਿਲਾਂ

English: a minute ago

{{value}} ਮਿੰਟ ਪਹਿਲਾਂ

English: {{value}} minutes ago

ਘੰਟਾ ਪਹਿਲਾਂ

English: an hour ago

{{value}} ਘੰਟੇ ਪਹਿਲਾਂ

English: {{value}} hours ago

ਇੱਕ ਦਿਨ ਪਹਿਲਾਂ

English: a day ago

{{value}} ਦਿਨ ਪਹਿਲਾਂ

English: {{value}} days ago

ਡੈਸ਼ਬੋਰਡ ਵੇਖਾਓ

Firefox/Fennec-specific: Show Dashboard

ਲੌਗਰ ਵੇਖਾਓ

Firefox/Fennec-specific: Show Logger

ਬੰਦ

Firefox-specific: appears as 'uBlock₀ (off)'

ਸਫ਼ੇ ਉੱਤੇ ਪਾਬੰਦੀ ਹੈ

Used as a title for the document-blocked page

uBlock Origin ਨੇ ਅੱਗੇ ਦਿੱਤੇ ਸਫ਼ੇ ਨੂੰ ਲੋਡ ਹੋਣ ਤੋਂ ਰੋਕਿਆ ਹੈ:

Used in the strict-blocking page

ਅੱਗੇ ਦਿੱਤੇ ਫਿਲਟਰ ਕਰਕੇ:

Used in the strict-blocking page

ਬਿਨਾਂ ਪੈਰਾਮੀਟਰ

label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png

ਲੱਭੋ:

English: List of filter list names follows

ਪਿੱਛੇ ਜਾਓ

English: Go back

ਇਹ ਵਿੰਡੋ ਬੰਦ ਕਰੋ

English: Close this window

ਇਸ ਸਾਈਟ ਬਾਰੇ ਮੈਨੂੰ ਮੁੜ ਚੇਤਾਵਨੀ ਨਾ ਦਿਓ

Label for checkbox in document-blocked page

{{hostname}} ਲਈ ਸਖ਼ਤ ਪਾਬੰਦੀਆਂ ਲਾਉਣਾ ਅਸਮਰੱਥ ਕਰੋ

English: Disable strict blocking for {{hostname}} ...

ਆਰਜ਼ੀ

English: Temporarily

ਪੱਕੇ ਤੌਰ ਉੱਤੇ

English: Permanently

ਜਾਰੀ ਰੱਖੋ

Button text to navigate to the blocked page

ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਉੱਤੇ ਐਕਸਪੋਰਟ ਕਰੋ

tooltip

ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਤੋਂ ਇੰਪੋਰਟ ਕਰੋ

tooltip

ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਤੋਂ ਇੰਪੋਰਟ ਕਰੋ ਅਤੇ ਮੌਜੂਦਾ ਸੈਟਿੰਗਾਂ ਨਾਲ ਮਿਲਾਓ

tooltip

ਇਸ ਡਿਵਾਈਸ ਦਾ ਨਾਂ:

used as a prompt for the user to provide a custom device name

ਸਾਵਧਾਨ! ਇਹ ਤਕਨੀਕੀ ਸੈਟਿੰਗਾਂ ਨੂੰ ਆਪਣੇ ਖ਼ਤਰੇ ਮੁਤਾਬਕ ਬਦਲੋ।

A warning to users at the top of 'Advanced settings' page

ਭੇਜੋ

for generic 'Submit' buttons

ਤਬਦੀਲੀਆਂ ਲਾਗੂ ਕਰੋ

for generic 'Apply changes' buttons

ਵਾਪਸ ਲਵੋ

for generic 'Revert' buttons

ਬਾਈਟ

ਫਰੇਮ ਵਿੱਚ ਭਾਗ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਈ…

An entry in the browser's contextual menu

ਫਿਲਟਰ ਸੂਚੀ ਲਈ ਮੈਂਬਰ ਬਣੋ…

ਆਰਜ਼ੀ ਤੌਰ ਉੱਤੇ ਵੱਡੇ ਮੀਡੀਆ ਭਾਗਾਂ ਨੂੰ ਮਨਜ਼ੂਰੀ ਦਿਓ

A context menu entry, present when large media elements have been blocked on the current site

ਸਰੋਤ ਕੋਡ ਵੇਖੋ…

A context menu entry, to view the source code of the target resource

ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ

Placeholder string for input field used to capture a keyboard shortcut

Toggle locked scrolling

Tooltip for the button used to lock scrolling between the views in the 'My rules' pane

ਕਲਿੱਪਬੋਰਡ ਵਿੱਚ ਕਾਪੀ ਕਰੋ

Label for buttons used to copy something to the clipboard

ਸਭ ਚੁਣੋ

Label for buttons used to select all text in editor

Toggle cosmetic filtering

Label for keyboard shortcut used to toggle cosmetic filtering

Toggle JavaScript

Label for keyboard shortcut used to toggle no-scripting switch

Relax blocking mode

Label for keyboard shortcut used to relax blocking mode

ਵਰਤੀ ਗਈ ਸਟੋਰੇਜ਼: {{value}} {{unit}}

In Setting pane, renders as (example): Storage used: 13.2 MB

KB

short for 'kilobytes'

MB

short for 'megabytes'

GB

short for 'gigabytes'

ਲੋਡ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ

Message used in frame placeholders

ਗਲਤੀਆਂ: {{count}}

Summary of number of errors as reported by the linter

Could not filter properly at browser launch. Reload the page to ensure proper filtering.

A warning which will appear in the popup panel if needed

This entry must be the last one

so we dont need to deal with comma for last entry


exec(input) {
try {
return super.exec(input);
} catch (ex) {
}
return [];
}

uBlock Origin Lite

extension name.

حاجب محتوى و بأقل التراخيص المسبقة. يحجب الإعلانات والمتتبعات والمعدنات والكثير فوراً عند التثبيت.

this will be in the Chrome web store: must be 132 characters or less

{{ruleCount}} شرط محولة من {{filterCount}} مصفيّات الشبكة

Appears aside each filter list in the _3rd-party filters_ pane

لوحة التحكم

English: uBO Lite — Dashboard

الإعدادات

appears as tab name in dashboard

عن التطبيق

appears as tab name in dashboard

سياسة الخصوصية

Link to privacy policy on GitHub (English)

وضع التصفية

Label in the popup panel for the current filtering mode

افتح لوحة التحكم

English: Click to open the dashboard

المزيد

Label to be used to show popup panel sections

أقل

Label to be used to hide popup panel sections

افتراضي

Header for a ruleset section in 'Filter lists pane'

اعلانات

الخصوصية

نطاقات البرامج الضارة

مضايقات

متنوع

المناطق واللغات

سجل التغييرات

شفرة المصدر (GPLv3)

English: Source code (GPLv3)

المساهمون

English: Contributors

شيفرة المصدر

Link text to source code repo

الترجمات

Link text to translations repo

قوائم التصفية

Link text to uBO's own filter lists repo

التبعيات الخارجية (متوافقة مع GPLv3):

Shown in the About pane

مرحبا

The header text for the welcome message section

لقد قمت للتو بتثبيت uBO Lite. هنا يمكنك اختيار وضع التصفية الافتراضي لاستخدامه في جميع مواقع الويب.

بشكل افتراضي ، يتم تحديد الوضع الأساسي لأنه لا يتطلب الإذن لقراءة البيانات وتعديلها. إذا كنت تثق في uBO Lite ، فيمكنك منحه إذنًا واسعًا لقراءة البيانات وتعديلها على جميع مواقع الويب من أجل تمكين المزيد من إمكانات التصفية المتقدمة لجميع مواقع الويب افتراضيًا.

Descriptive text shown at first install time only

وضع التصفية الافتراضي

The header text for the default filtering mode section

سيتم تجاوز وضع التصفية الافتراضي بواسطة أوضاع التصفية لكل موقع ويب. يمكنك ضبط وضع التصفية على أي موقع ويب معين وفقًا للوضع الذي يعمل بشكل أفضل على موقع الويب هذا. كل وضع له مزاياه وعيوبه.

This describes the default filtering mode setting

لا تصفية

Name of blocking mode 0

أساسي

Name of blocking mode 1

الأفضل

Name of blocking mode 2

مكتمل

Name of blocking mode 3

تصفية الشبكة الأساسية من قوائم التصفية المحددة.

لا يتطلب إذنًا لقراءة البيانات وتعديلها على مواقع الويب.

This describes the 'basic' filtering mode

تصفية الشبكة المتقدمة بالإضافة إلى تصفية موسعة محددة من قوائم التصفية المحددة.

يتطلب إذنًا واسعًا لقراءة البيانات وتعديلها على جميع مواقع الويب.

This describes the 'optimal' filtering mode

تصفية الشبكة المتقدمة بالإضافة إلى تصفية موسعة محددة وعامة من قوائم التصفية المحددة.

يتطلب إذنًا واسعًا لقراءة البيانات وتعديلها على جميع مواقع الويب.

قد تؤدي التصفية الموسعة العامة إلى زيادة استخدام موارد صفحة الويب.

This describes the 'complete' filtering mode

قائمة بأسماء المضيفين التي لن تتم أي تصفية لها

A short description for the editable field which lists trusted sites

السلوك

The header text for the 'Behavior' section

إعادة تحميل الصفحة تلقائيًا عند تغيير وضع التصفية

Label for a checkbox in the options page


if ( line === `!#trusted on ${context.secret}` ) {

} else if ( line === `!#trusted off ${context.secret}` ) {

/*******************************************************************************

    uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
    Copyright (C) 2014-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

/* jshint esversion:11 */

'use strict';

/******************************************************************************/

// Important!
// Isolate from global scope
(function uBOL_cssProcedural() {

/******************************************************************************/

const proceduralImports = self.proceduralImports || [];
self.proceduralImports = undefined;
delete self.proceduralImports;

/******************************************************************************/

const hnParts = [];
try { hnParts.push(...document.location.hostname.split('.')); }
catch(ex) { }
const hnpartslen = hnParts.length;
if ( hnpartslen === 0 ) { return; }

const selectors = [];

for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of proceduralImports ) {
    const todoIndices = new Set();
    const tonotdoIndices = [];
    // Exceptions
    if ( exceptionsMap.size !== 0 ) {
        for ( let i = 0; i < hnpartslen; i++ ) {
            const hn = hnParts.slice(i).join('.');
            const excepted = exceptionsMap.get(hn);
            if ( excepted ) { tonotdoIndices.push(...excepted); }
        }
        exceptionsMap.clear();
    }
    // Hostname-based
    if ( hostnamesMap.size !== 0 ) {
        const collectArgIndices = hn => {
            let argsIndices = hostnamesMap.get(hn);
            if ( argsIndices === undefined ) { return; }
            if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
            for ( const argsIndex of argsIndices ) {
                if ( tonotdoIndices.includes(argsIndex) ) { continue; }
                todoIndices.add(argsIndex);
            }
        };
        for ( let i = 0; i < hnpartslen; i++ ) {
            const hn = hnParts.slice(i).join('.');
            collectArgIndices(hn);
        }
        collectArgIndices('*');
        hostnamesMap.clear();
    }
    // Entity-based
    if ( entitiesMap.size !== 0 ) {
        const n = hnpartslen - 1;
        for ( let i = 0; i < n; i++ ) {
            for ( let j = n; j > i; j-- ) {
                const en = hnParts.slice(i,j).join('.');
                let argsIndices = entitiesMap.get(en);
                if ( argsIndices === undefined ) { continue; }
                if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; }
                for ( const argsIndex of argsIndices ) {
                    if ( tonotdoIndices.includes(argsIndex) ) { continue; }
                    todoIndices.add(argsIndex);
                }
            }
        }
        entitiesMap.clear();
    }
    for ( const i of todoIndices ) {
        selectors.push(...argsList[i].map(json => JSON.parse(json)));
    }
    argsList.length = 0;
}
proceduralImports.length = 0;

if ( selectors.length === 0 ) { return; }

/******************************************************************************/

const uBOL_injectCSS = (css, count = 10) => {
    chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => {
        count -= 1;
        if ( count === 0 ) { return; }
        uBOL_injectCSS(css, count - 1);
    });
};

const nonVisualElements = {
    script: true,
    style: true,
};

const regexFromString = (s, exact = false) => {
    if ( s === '' ) { return /^/; }
    const match = /^\/(.+)\/([i]?)$/.exec(s);
    if ( match !== null ) {
        return new RegExp(match[1], match[2] || undefined);
    }
    const reStr = s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    return new RegExp(exact ? `^${reStr}$` : reStr, 'i');
};

/******************************************************************************/

// 'P' stands for 'Procedural'

class PSelectorTask {
    begin() {
    }
    end() {
    }
}

/******************************************************************************/

class PSelectorVoidTask extends PSelectorTask {
    constructor(task) {
        super();
        console.info(`uBO: :${task[0]}() operator does not exist`);
    }
    transpose() {
    }
}

/******************************************************************************/

class PSelectorHasTextTask extends PSelectorTask {
    constructor(task) {
        super();
        this.needle = regexFromString(task[1]);
    }
    transpose(node, output) {
        if ( this.needle.test(node.textContent) ) {
            output.push(node);
        }
    }
}

/******************************************************************************/

class PSelectorIfTask extends PSelectorTask {
    constructor(task) {
        super();
        this.pselector = new PSelector(task[1]);
    }
    transpose(node, output) {
        if ( this.pselector.test(node) === this.target ) {
            output.push(node);
        }
    }
}
PSelectorIfTask.prototype.target = true;

class PSelectorIfNotTask extends PSelectorIfTask {
}
PSelectorIfNotTask.prototype.target = false;

/******************************************************************************/

class PSelectorMatchesAttrTask extends PSelectorTask {
    constructor(task) {
        super();
        this.reAttr = regexFromString(task[1].attr, true);
        this.reValue = regexFromString(task[1].value, true);
    }
    transpose(node, output) {
        const attrs = node.getAttributeNames();
        for ( const attr of attrs ) {
            if ( this.reAttr.test(attr) === false ) { continue; }
            if ( this.reValue.test(node.getAttribute(attr)) === false ) { continue; }
            output.push(node);
        }
    }
}

/******************************************************************************/

class PSelectorMatchesCSSTask extends PSelectorTask {
    constructor(task) {
        super();
        this.name = task[1].name;
        this.pseudo = task[1].pseudo ? `::${task[1].pseudo}` : null;
        let arg0 = task[1].value, arg1;
        if ( Array.isArray(arg0) ) {
            arg1 = arg0[1]; arg0 = arg0[0];
        }
        this.value = new RegExp(arg0, arg1);
    }
    transpose(node, output) {
        const style = window.getComputedStyle(node, this.pseudo);
        if ( style !== null && this.value.test(style[this.name]) ) {
            output.push(node);
        }
    }
}
class PSelectorMatchesCSSAfterTask extends PSelectorMatchesCSSTask {
    constructor(task) {
        super(task);
        this.pseudo = '::after';
    }
}

class PSelectorMatchesCSSBeforeTask extends PSelectorMatchesCSSTask {
    constructor(task) {
        super(task);
        this.pseudo = '::before';
    }
}

/******************************************************************************/

class PSelectorMatchesMediaTask extends PSelectorTask {
    constructor(task) {
        super();
        this.mql = window.matchMedia(task[1]);
        if ( this.mql.media === 'not all' ) { return; }
        this.mql.addEventListener('change', ( ) => {
            if ( proceduralFilterer instanceof Object === false ) { return; }
            proceduralFilterer.onDOMChanged([ null ]);
        });
    }
    transpose(node, output) {
        if ( this.mql.matches === false ) { return; }
        output.push(node);
    }
}

/******************************************************************************/

class PSelectorMatchesPathTask extends PSelectorTask {
    constructor(task) {
        super();
        this.needle = regexFromString(
            task[1].replace(/\P{ASCII}/gu, s => encodeURIComponent(s))
        );
    }
    transpose(node, output) {
        if ( this.needle.test(self.location.pathname + self.location.search) ) {
            output.push(node);
        }
    }
}

/******************************************************************************/

class PSelectorMinTextLengthTask extends PSelectorTask {
    constructor(task) {
        super();
        this.min = task[1];
    }
    transpose(node, output) {
        if ( node.textContent.length >= this.min ) {
            output.push(node);
        }
    }
}

/******************************************************************************/

class PSelectorOthersTask extends PSelectorTask {
    constructor() {
        super();
        this.targets = new Set();
    }
    begin() {
        this.targets.clear();
    }
    end(output) {
        const toKeep = new Set(this.targets);
        const toDiscard = new Set();
        const body = document.body;
        let discard = null;
        for ( let keep of this.targets ) {
            while ( keep !== null && keep !== body ) {
                toKeep.add(keep);
                toDiscard.delete(keep);
                discard = keep.previousElementSibling;
                while ( discard !== null ) {
                    if (
                        nonVisualElements[discard.localName] !== true &&
                        toKeep.has(discard) === false
                    ) {
                        toDiscard.add(discard);
                    }
                    discard = discard.previousElementSibling;
                }
                discard = keep.nextElementSibling;
                while ( discard !== null ) {
                    if (
                        nonVisualElements[discard.localName] !== true &&
                        toKeep.has(discard) === false
                    ) {
                        toDiscard.add(discard);
                    }
                    discard = discard.nextElementSibling;
                }
                keep = keep.parentElement;
            }
        }
        for ( discard of toDiscard ) {
            output.push(discard);
        }
        this.targets.clear();
    }
    transpose(candidate) {
        for ( const target of this.targets ) {
            if ( target.contains(candidate) ) { return; }
            if ( candidate.contains(target) ) {
                this.targets.delete(target);
            }
        }
        this.targets.add(candidate);
    }
}

/******************************************************************************/

// https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277
//   Prepend `:scope ` if needed.
class PSelectorSpathTask extends PSelectorTask {
    constructor(task) {
        super();
        this.spath = task[1];
        this.nth = /^(?:\s*[+~]|:)/.test(this.spath);
        if ( this.nth ) { return; }
        if ( /^\s*>/.test(this.spath) ) {
            this.spath = `:scope ${this.spath.trim()}`;
        }
    }
    transpose(node, output) {
        const nodes = this.nth
            ? PSelectorSpathTask.qsa(node, this.spath)
            : node.querySelectorAll(this.spath);
        for ( const node of nodes ) {
            output.push(node);
        }
    }
    // Helper method for other operators.
    static qsa(node, selector) {
        const parent = node.parentElement;
        if ( parent === null ) { return []; }
        let pos = 1;
        for (;;) {
            node = node.previousElementSibling;
            if ( node === null ) { break; }
            pos += 1;
        }
        return parent.querySelectorAll(
            `:scope > :nth-child(${pos})${selector}`
        );
    }
}

/******************************************************************************/

class PSelectorUpwardTask extends PSelectorTask {
    constructor(task) {
        super();
        const arg = task[1];
        if ( typeof arg === 'number' ) {
            this.i = arg;
        } else {
            this.s = arg;
        }
    }
    transpose(node, output) {
        if ( this.s !== '' ) {
            const parent = node.parentElement;
            if ( parent === null ) { return; }
            node = parent.closest(this.s);
            if ( node === null ) { return; }
        } else {
            let nth = this.i;
            for (;;) {
                node = node.parentElement;
                if ( node === null ) { return; }
                nth -= 1;
                if ( nth === 0 ) { break; }
            }
        }
        output.push(node);
    }
}
PSelectorUpwardTask.prototype.i = 0;
PSelectorUpwardTask.prototype.s = '';

/******************************************************************************/

class PSelectorWatchAttrs extends PSelectorTask {
    constructor(task) {
        super();
        this.observer = null;
        this.observed = new WeakSet();
        this.observerOptions = {
            attributes: true,
            subtree: true,
        };
        const attrs = task[1];
        if ( Array.isArray(attrs) && attrs.length !== 0 ) {
            this.observerOptions.attributeFilter = task[1];
        }
    }
    // TODO: Is it worth trying to re-apply only the current selector?
    handler() {
        if ( proceduralFilterer instanceof Object ) {
            proceduralFilterer.onDOMChanged([ null ]);
        }
    }
    transpose(node, output) {
        output.push(node);
        if ( this.observed.has(node) ) { return; }
        if ( this.observer === null ) {
            this.observer = new MutationObserver(this.handler);
        }
        this.observer.observe(node, this.observerOptions);
        this.observed.add(node);
    }
}

/******************************************************************************/

class PSelectorXpathTask extends PSelectorTask {
    constructor(task) {
        super();
        this.xpe = document.createExpression(task[1], null);
        this.xpr = null;
    }
    transpose(node, output) {
        this.xpr = this.xpe.evaluate(
            node,
            XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
            this.xpr
        );
        let j = this.xpr.snapshotLength;
        while ( j-- ) {
            const node = this.xpr.snapshotItem(j);
            if ( node.nodeType === 1 ) {
                output.push(node);
            }
        }
    }
}

/******************************************************************************/

class PSelector {
    constructor(o) {
        this.raw = o.raw;
        this.selector = o.selector;
        this.tasks = [];
        const tasks = [];
        if ( Array.isArray(o.tasks) === false ) { return; }
        for ( const task of o.tasks ) {
            const ctor = this.operatorToTaskMap.get(task[0]) || PSelectorVoidTask;
            tasks.push(new ctor(task));
        }
        this.tasks = tasks;
    }
    prime(input) {
        const root = input || document;
        if ( this.selector === '' ) { return [ root ]; }
        if ( input !== document ) {
            const c0 = this.selector.charCodeAt(0);
            if ( c0 === 0x2B /* + */ || c0 === 0x7E /* ~ */ ) {
                return Array.from(PSelectorSpathTask.qsa(input, this.selector));
            } else if ( c0 === 0x3E /* > */ ) {
                return Array.from(input.querySelectorAll(`:scope ${this.selector}`));
            }
        }
        return Array.from(root.querySelectorAll(this.selector));
    }
    exec(input) {
        let nodes = this.prime(input);
        for ( const task of this.tasks ) {
            if ( nodes.length === 0 ) { break; }
            const transposed = [];
            task.begin();
            for ( const node of nodes ) {
                task.transpose(node, transposed);
            }
            task.end(transposed);
            nodes = transposed;
        }
        return nodes;
    }
    test(input) {
        const nodes = this.prime(input);
        for ( const node of nodes ) {
            let output = [ node ];
            for ( const task of this.tasks ) {
                const transposed = [];
                task.begin();
                for ( const node of output ) {
                    task.transpose(node, transposed);
                }
                task.end(transposed);
                output = transposed;
                if ( output.length === 0 ) { break; }
            }
            if ( output.length !== 0 ) { return true; }
        }
        return false;
    }
}
PSelector.prototype.operatorToTaskMap = new Map([
    [ 'has', PSelectorIfTask ],
    [ 'has-text', PSelectorHasTextTask ],
    [ 'if', PSelectorIfTask ],
    [ 'if-not', PSelectorIfNotTask ],
    [ 'matches-attr', PSelectorMatchesAttrTask ],
    [ 'matches-css', PSelectorMatchesCSSTask ],
    [ 'matches-css-after', PSelectorMatchesCSSAfterTask ],
    [ 'matches-css-before', PSelectorMatchesCSSBeforeTask ],
    [ 'matches-media', PSelectorMatchesMediaTask ],
    [ 'matches-path', PSelectorMatchesPathTask ],
    [ 'min-text-length', PSelectorMinTextLengthTask ],
    [ 'not', PSelectorIfNotTask ],
    [ 'others', PSelectorOthersTask ],
    [ 'spath', PSelectorSpathTask ],
    [ 'upward', PSelectorUpwardTask ],
    [ 'watch-attr', PSelectorWatchAttrs ],
    [ 'xpath', PSelectorXpathTask ],
]);

/******************************************************************************/

class PSelectorRoot extends PSelector {
    constructor(o) {
        super(o);
        this.budget = 200; // I arbitrary picked a 1/5 second
        this.raw = o.raw;
        this.cost = 0;
        this.lastAllowanceTime = 0;
        this.action = o.action;
    }
    prime(input) {
        try {
            return super.prime(input);
        } catch (ex) {
        }
        return [];
    }
}

/******************************************************************************/

class ProceduralFilterer {
    constructor(selectors) {
        this.selectors = [];
        this.masterToken = this.randomToken();
        this.styleTokenMap = new Map();
        this.styledNodes = new Set();
        this.timer = undefined;
        this.hideStyle = 'display:none!important;';
        this.addSelectors(selectors);
        // Important: commit now (do not go through onDOMChanged) to be sure
        // first pass is going to happen asap.
        this.uBOL_commitNow();
    }

    addSelectors() {
        for ( const selector of selectors ) {
            const pselector = new PSelectorRoot(selector);
            this.primeProceduralSelector(pselector);
            this.selectors.push(pselector);
        }
        this.onDOMChanged();
    }

    // This allows to perform potentially expensive initialization steps
    // before the filters are ready to be applied.
    primeProceduralSelector(pselector) {
        if ( pselector.action === undefined ) {
            this.styleTokenFromStyle(this.hideStyle);
        } else if ( pselector.action[0] === 'style' ) {
            this.styleTokenFromStyle(pselector.action[1]);
        }
        return pselector;
    }

    uBOL_commitNow() {
        // https://github.com/uBlockOrigin/uBlock-issues/issues/341
        //   Be ready to unhide nodes which no longer matches any of
        //   the procedural selectors.
        const toUnstyle = this.styledNodes;
        this.styledNodes = new Set();

        let t0 = Date.now();

        for ( const pselector of this.selectors.values() ) {
            const allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);
            if ( allowance >= 1 ) {
                pselector.budget += allowance * 50;
                if ( pselector.budget > 200 ) { pselector.budget = 200; }
                pselector.lastAllowanceTime = t0;
            }
            if ( pselector.budget <= 0 ) { continue; }
            const nodes = pselector.exec();
            const t1 = Date.now();
            pselector.budget += t0 - t1;
            if ( pselector.budget < -500 ) {
                console.info('uBOL: disabling %s', pselector.raw);
                pselector.budget = -0x7FFFFFFF;
            }
            t0 = t1;
            if ( nodes.length === 0 ) { continue; }
            this.processNodes(nodes, pselector.action);
        }

        this.unprocessNodes(toUnstyle);
    }

    styleTokenFromStyle(style) {
        if ( style === undefined ) { return; }
        let styleToken = this.styleTokenMap.get(style);
        if ( styleToken !== undefined ) { return styleToken; }
        styleToken = this.randomToken();
        this.styleTokenMap.set(style, styleToken);
        uBOL_injectCSS(`[${this.masterToken}][${styleToken}]\n{${style}}\n`);
        return styleToken;
    }

    processNodes(nodes, action) {
        const op = action && action[0] || '';
        const arg = op !== '' ? action[1] : '';
        switch ( op ) {
        case '':
            /* fall through */
        case 'style': {
            const styleToken = this.styleTokenFromStyle(
                arg === '' ? this.hideStyle : arg
            );
            for ( const node of nodes ) {
                node.setAttribute(this.masterToken, '');
                node.setAttribute(styleToken, '');
                this.styledNodes.add(node);
            }
            break;
        }
        case 'remove': {
            for ( const node of nodes ) {
                node.remove();
                node.textContent = '';
            }
            break;
        }
        case 'remove-attr': {
            const reAttr = regexFromString(arg, true);
            for ( const node of nodes ) {
                for ( const name of node.getAttributeNames() ) {
                    if ( reAttr.test(name) === false ) { continue; }
                    node.removeAttribute(name);
                }
            }
            break;
        }
        case 'remove-class': {
            const reClass = regexFromString(arg, true);
            for ( const node of nodes ) {
                const cl = node.classList;
                for ( const name of cl.values() ) {
                    if ( reClass.test(name) === false ) { continue; }
                    cl.remove(name);
                }
            }
            break;
        }
        default:
            break;
        }
    }

    // TODO: Current assumption is one style per hit element. Could be an
    //       issue if an element has multiple styling and one styling is
    //       brought back. Possibly too rare to care about this for now.
    unprocessNodes(nodes) {
        for ( const node of nodes ) {
            if ( this.styledNodes.has(node) ) { continue; }
            node.removeAttribute(this.masterToken);
        }
    }

    randomToken() {
        const n = Math.random();
        return String.fromCharCode(n * 25 + 97) +
            Math.floor(
                (0.25 + n * 0.75) * Number.MAX_SAFE_INTEGER
            ).toString(36).slice(-8);
    }

    onDOMChanged() {
        if ( this.timer !== undefined ) { return; }
        this.timer = self.requestAnimationFrame(( ) => {
            this.timer = undefined;
            this.uBOL_commitNow();
        });
    }
}

/******************************************************************************/

const proceduralFilterer = new ProceduralFilterer(selectors);

const observer = new MutationObserver(mutations => {
    let domChanged = false;
    for ( let i = 0; i < mutations.length && !domChanged; i++ ) {
        const mutation = mutations[i];
        for ( const added of mutation.addedNodes ) {
            if ( added.nodeType !== 1 ) { continue; }
            domChanged = true;
            break;
        }
        if ( domChanged === false ) {
            for ( const removed of mutation.removedNodes ) {
                if ( removed.nodeType !== 1 ) { continue; }
                domChanged = true;
                break;
            }
        }
    }
    if ( domChanged === false ) { return; }
    proceduralFilterer.onDOMChanged();
});

observer.observe(document, {
    childList: true,
    subtree: true,
});

/******************************************************************************/

})();

/******************************************************************************/

void 0;


uBO Lite (uBOL) is a *permission-less* MV3-based content blocker.

The default ruleset corresponds to uBlock Origin's default filterset:

- uBlock Origin's built-in filter lists
- EasyList
- EasyPrivacy
- Peter Lowe’s Ad and tracking server list

You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel.

uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages.

uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time.

However, uBOL allows you to *explicitly* grant extended permissions on specific sites of your choice so that it can better filter on those sites using cosmetic filtering and scriptlet injections.

To grant extended permissions on a given site, open the popup panel and pick a higher filtering mode such as Optimal or Complete.

The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request.

If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site.

You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites.

Keep in mind this is still a work in progress, with these end goals:

- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis.

- Entirely declarative for reliability and CPU/memory efficiency.


/*******************************************************************************

    uBlock Origin Lite - a comprehensive, MV3-compliant content blocker
    Copyright (C) 2014-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

'use strict';

import { runtime } from './ext.js';
import { dom } from './dom.js';

/******************************************************************************/

{
    const manifest = runtime.getManifest();
    dom.text('#aboutNameVer', `${manifest.name} ${manifest.version}`);
}

dom.attr('a', 'target', '_blank');

dom.on('#dashboard-nav', 'click', '.tabButton', ev => {
    dom.body.dataset.pane = ev.target.dataset.pane;
});

/******************************************************************************/


/*******************************************************************************

    uBlock Origin - a comprehensive, efficient content blocker
    Copyright (C) 2014-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

'use strict';

/******************************************************************************/

import { strict as assert } from 'assert';
import { readFile } from 'fs/promises';
import { createRequire } from 'module';
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';

import { createWorld } from 'esm-world';

import './_common.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

const require = createRequire(import.meta.url);

const requests = require('scaling-palm-tree/requests.json');
const results = require('./data/results.json');

async function read(path) {
    return readFile(resolve(__dirname, path), 'utf8');
}

describe('Request data', () => {
    const typeMap = {
        document: 'sub_frame',
        stylesheet: 'stylesheet',
        image: 'image',
        media: 'media',
        font: 'font',
        script: 'script',
        xhr: 'xmlhttprequest',
        fetch: 'xmlhttprequest',
        websocket: 'websocket',
        ping: 'ping',

        other: 'other',
        eventsource: 'other',
        manifest: 'other',
        texttrack: 'other',
    };

    for ( let wasm of [ false, true ] ) {
        context(`${wasm ? 'Wasm on' : 'Wasm off'}`, () => {
            let engine = null;

            before(async () => {
                const { StaticNetFilteringEngine, enableWASM } = await createWorld('./index.js', { globals: global });

                if ( wasm ) {
                    assert(await enableWASM());
                }

                engine = await StaticNetFilteringEngine.create();

                await engine.useLists([
                    read('./data/assets/ublock/badware.txt')
                        .then(raw => ({ name: 'badware', raw })),
                    read('./data/assets/ublock/filters.txt')
                        .then(raw => ({ name: 'filters', raw })),
                    read('./data/assets/ublock/filters-2020.txt')
                        .then(raw => ({ name: 'filters-2020', raw })),
                    read('./data/assets/ublock/filters-2021.txt')
                        .then(raw => ({ name: 'filters-2021', raw })),
                    read('./data/assets/ublock/privacy.txt')
                        .then(raw => ({ name: 'privacy', raw })),
                    read('./data/assets/ublock/resource-abuse.txt')
                        .then(raw => ({ name: 'resource-abuse', raw })),
                    read('./data/assets/ublock/unbreak.txt')
                        .then(raw => ({ name: 'unbreak.txt', raw })),
                    read('./data/assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt')
                        .then(raw => ({ name: 'easylist', raw })),
                    read('./data/assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt')
                        .then(raw => ({ name: 'easyprivacy', raw })),
                    read('./data/assets/thirdparties/pgl.yoyo.org/as/serverlist')
                        .then(raw => ({ name: 'PGL', raw })),
                    read('./data/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt')
                        .then(raw => ({ name: 'urlhaus', raw })),
                ]);
            });

            for ( let i = 0; i < requests.length; i++ ) {
                const { url, frameUrl, cpt } = requests[i];
                const request = { url, originURL: frameUrl, type: typeMap[cpt] };

                const expected = results[i];

                it(`should ${expected === 1 ? 'block' : 'allow'} ${request.type} URL ${request.url} from origin ${request.originURL}`, () => {
                    assert.equal(engine.matchRequest(request), expected);
                });
            }
        });
    }
});


keep keep replace keep keep keep keep keep

/*******************************************************************************

    uBlock Origin - a comprehensive, efficient content blocker
    Copyright (C) 2014-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

'use strict';

/******************************************************************************/

import staticNetFilteringEngine from './static-net-filtering.js';
import { LineIterator } from './text-utils.js';
import * as sfp from './static-filtering-parser.js';

import {
    CompiledListReader,
    CompiledListWriter,
} from './static-filtering-io.js';

/******************************************************************************/

// http://www.cse.yorku.ca/~oz/hash.html#djb2
//   Must mirror content script surveyor's version

const hashFromStr = (type, s) => {
    const len = s.length;
    const step = len + 7 >>> 3;
    let hash = (type << 5) + type ^ len;
    for ( let i = 0; i < len; i += step ) {
        hash = (hash << 5) + hash ^ s.charCodeAt(i);
    }
    return hash & 0xFFFFFF;
};

/******************************************************************************/

// Copied from cosmetic-filter.js for the time being to avoid unwanted
// dependencies

const rePlainSelector = /^[#.][\w\\-]+/;
const rePlainSelectorEx = /^[^#.\[(]+([#.][\w-]+)|([#.][\w-]+)$/;
const rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/;
const reEscapeSequence = /\\([0-9A-Fa-f]+ |.)/g;

const keyFromSelector = selector => {
    let key = '';
    let matches = rePlainSelector.exec(selector);
    if ( matches ) {
        key = matches[0];
    } else {
        matches = rePlainSelectorEx.exec(selector);
        if ( matches === null ) { return; }
        key = matches[1] || matches[2];
    }
    if ( key.indexOf('\\') === -1 ) { return key; }
    matches = rePlainSelectorEscaped.exec(selector);
    if ( matches === null ) { return; }
    key = '';
    const escaped = matches[0];
    let beg = 0;
    reEscapeSequence.lastIndex = 0;
    for (;;) {
        matches = reEscapeSequence.exec(escaped);
        if ( matches === null ) {
            return key + escaped.slice(beg);
        }
        key += escaped.slice(beg, matches.index);
        beg = reEscapeSequence.lastIndex;
        if ( matches[1].length === 1 ) {
            key += matches[1];
        } else {
            key += String.fromCharCode(parseInt(matches[1], 16));
        }
    }
};

/******************************************************************************/

function addExtendedToDNR(context, parser) {
    if ( parser.isExtendedFilter() === false ) { return false; }

    // Scriptlet injection
    if ( parser.isScriptletFilter() ) {
        if ( parser.hasOptions() === false ) { return; }
        if ( context.scriptletFilters === undefined ) {
            context.scriptletFilters = new Map();
        }
        const exception = parser.isException();
        const args = parser.getScriptletArgs();
        const argsToken = JSON.stringify(args);
        for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) {
            if ( bad ) { continue; }
            if ( exception ) { continue; }
            let details = context.scriptletFilters.get(argsToken);
            if ( details === undefined ) {
                context.scriptletFilters.set(argsToken, details = { args });
                if ( context.trustedSource ) {
                    details.trustedSource = true;
                }
            }
            if ( not ) {
                if ( details.excludeMatches === undefined ) {
                    details.excludeMatches = [];
                }
                details.excludeMatches.push(hn);
                continue;
            }
            if ( details.matches === undefined ) {
                details.matches = [];
            }
            if ( details.matches.includes('*') ) { continue; }
            if ( hn === '*' ) {
                details.matches = [ '*' ];
                continue;
            }
            details.matches.push(hn);
        }
        return;
    }

    // Response header filtering
    if ( parser.isResponseheaderFilter() ) {
        if ( parser.hasError() ) { return; }
        if ( parser.hasOptions() === false ) { return; }
        if ( parser.isException() ) { return; }
        const node = parser.getBranchFromType(sfp.NODE_TYPE_EXT_PATTERN_RESPONSEHEADER);
        if ( node === 0 ) { return; }
        const header = parser.getNodeString(node);
        if ( context.responseHeaderRules === undefined ) {
            context.responseHeaderRules = [];
        }
        const rule =  {
            action: {
                responseHeaders: [
                    {
                        header,
                        operation: 'remove',
                    }
                ],
                type: 'modifyHeaders'
            },
            condition: {
                resourceTypes: [
                    'main_frame',
                    'sub_frame'
                ]
            },
        };
        for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) {
            if ( bad ) { continue; }
            if ( not ) {
                if ( rule.condition.excludedInitiatorDomains === undefined ) {
                    rule.condition.excludedInitiatorDomains = [];
                }
                rule.condition.excludedInitiatorDomains.push(hn);
                continue;
            }
            if ( hn === '*' ) {
                if ( rule.condition.initiatorDomains !== undefined ) {
                    rule.condition.initiatorDomains = undefined;
                }
                continue;
            }
            if ( rule.condition.initiatorDomains === undefined ) {
                rule.condition.initiatorDomains = [];
            }
            rule.condition.initiatorDomains.push(hn);
        }
        context.responseHeaderRules.push(rule);
        return;
    }

    // HTML filtering
    if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
        return;
    }

    // Cosmetic filtering

    // Generic cosmetic filtering
    if ( parser.hasOptions() === false ) {
        const { compiled } = parser.result;
        if ( compiled === undefined ) { return; }
        if ( compiled.length <= 1 ) { return; }
        if ( parser.isException() ) {
            if ( context.genericCosmeticExceptions === undefined ) {
                context.genericCosmeticExceptions = new Set();
            }
            context.genericCosmeticExceptions.add(compiled);
            return;
        }
        if ( compiled.charCodeAt(0) === 0x7B /* '{' */ ) { return; }
        const key = keyFromSelector(compiled);
        if ( key === undefined ) {
            if ( context.genericHighCosmeticFilters === undefined ) {
                context.genericHighCosmeticFilters = new Set();
            }
            context.genericHighCosmeticFilters.add(compiled);
            return;
        }
        const type = key.charCodeAt(0);
        const hash = hashFromStr(type, key.slice(1));
        if ( context.genericCosmeticFilters === undefined ) {
            context.genericCosmeticFilters = new Map();
        }
        let bucket = context.genericCosmeticFilters.get(hash);
        if ( bucket === undefined ) {
            context.genericCosmeticFilters.set(hash, bucket = []);
        }
        bucket.push(compiled);
        return;
    }

    // Specific cosmetic filtering
    // https://github.com/chrisaljoudi/uBlock/issues/151
    //   Negated hostname means the filter applies to all non-negated hostnames
    //   of same filter OR globally if there is no non-negated hostnames.
    if ( context.specificCosmeticFilters === undefined ) {
        context.specificCosmeticFilters = new Map();
    }
    for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) {
        if ( bad ) { continue; }
        let { compiled, exception, raw } = parser.result;
        if ( exception ) { continue; }
        let rejected;
        if ( compiled === undefined ) {
            rejected = `Invalid filter: ${hn}##${raw}`;
        }
        if ( rejected ) {
            compiled = rejected;
        }
        let details = context.specificCosmeticFilters.get(compiled);
        if ( details === undefined ) {
            details = {};
            if ( rejected ) { details.rejected = true; }
            context.specificCosmeticFilters.set(compiled, details);
        }
        if ( rejected ) { continue; }
        if ( not ) {
            if ( details.excludeMatches === undefined ) {
                details.excludeMatches = [];
            }
            details.excludeMatches.push(hn);
            continue;
        }
        if ( details.matches === undefined ) {
            details.matches = [];
        }
        if ( details.matches.includes('*') ) { continue; }
        if ( hn === '*' ) {
            details.matches = [ '*' ];
            continue;
        }
        details.matches.push(hn);
    }
}

/******************************************************************************/

function addToDNR(context, list) {
    const env = context.env || [];
    const writer = new CompiledListWriter();
    const lineIter = new LineIterator(
        sfp.utils.preparser.prune(list.text, env)
    );
    const parser = new sfp.AstFilterParser({
        toDNR: true,
        nativeCssHas: env.includes('native_css_has'),
        badTypes: [ sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ],
    });
    const compiler = staticNetFilteringEngine.createCompiler();

    writer.properties.set('name', list.name);
    compiler.start(writer);

    while ( lineIter.eot() === false ) {
        let line = lineIter.next();
        while ( line.endsWith(' \\') ) {
            if ( lineIter.peek(4) !== '    ' ) { break; }
            line = line.slice(0, -2).trim() + lineIter.next().trim();
        }

        parser.parse(line);

        if ( parser.isComment() ) {
            if ( line === `!#trusted on ${context.secret}` ) {
                parser.trustedSource = true;
                context.trustedSource = true;
            } else if ( line === `!#trusted off ${context.secret}` ) {
                parser.trustedSource = false;
                context.trustedSource = false;
            }
            continue;
        }

        if ( parser.isFilter() === false ) { continue; }
        if ( parser.hasError() ) {
            if ( parser.astError === sfp.AST_ERROR_OPTION_EXCLUDED ) {
                context.invalid.add(`Incompatible with DNR: ${line}`);
            }
            continue;
        }

        if ( parser.isExtendedFilter() ) {
            addExtendedToDNR(context, parser);
            continue;
        }
        if ( parser.isNetworkFilter() === false ) { continue; }

        if ( compiler.compile(parser, writer) ) { continue; }

        if ( compiler.error !== undefined ) {
            context.invalid.add(compiler.error);
        }
    }

    compiler.finish(writer);

    staticNetFilteringEngine.dnrFromCompiled(
        'add',
        context,
        new CompiledListReader(writer.toString())
    );
}

/******************************************************************************/

function finalizeRuleset(context, network) {
    const ruleset = network.ruleset;

    // Assign rule ids
    const rulesetMap = new Map();
    {
        let ruleId = 1;
        for ( const rule of ruleset ) {
            rulesetMap.set(ruleId++, rule);
        }
    }
    // Merge rules where possible by merging arrays of a specific property.
    //
    // https://github.com/uBlockOrigin/uBOL-home/issues/10#issuecomment-1304822579
    //   Do not merge rules which have errors.
    const mergeRules = (rulesetMap, mergeTarget) => {
        const mergeMap = new Map();
        const sorter = (_, v) => {
            if ( Array.isArray(v) ) {
                return typeof v[0] === 'string' ? v.sort() : v;
            }
            if ( v instanceof Object ) {
                const sorted = {};
                for ( const kk of Object.keys(v).sort() ) {
                    sorted[kk] = v[kk];
                }
                return sorted;
            }
            return v;
        };
        const ruleHasher = (rule, target) => {
            return JSON.stringify(rule, (k, v) => {
                if ( k.startsWith('_') ) { return; }
                if ( k === target ) { return; }
                return sorter(k, v);
            });
        };
        const extractTargetValue = (obj, target) => {
            for ( const [ k, v ] of Object.entries(obj) ) {
                if ( Array.isArray(v) && k === target ) { return v; }
                if ( v instanceof Object ) {
                    const r = extractTargetValue(v, target);
                    if ( r !== undefined ) { return r; }
                }
            }
        };
        const extractTargetOwner = (obj, target) => {
            for ( const [ k, v ] of Object.entries(obj) ) {
                if ( Array.isArray(v) && k === target ) { return obj; }
                if ( v instanceof Object ) {
                    const r = extractTargetOwner(v, target);
                    if ( r !== undefined ) { return r; }
                }
            }
        };
        for ( const [ id, rule ] of rulesetMap ) {
            if ( rule._error !== undefined ) { continue; }
            const hash = ruleHasher(rule, mergeTarget);
            if ( mergeMap.has(hash) === false ) {
                mergeMap.set(hash, []);
            }
            mergeMap.get(hash).push(id);
        }
        for ( const ids of mergeMap.values() ) {
            if ( ids.length === 1 ) { continue; }
            const leftHand = rulesetMap.get(ids[0]);
            const leftHandSet = new Set(
                extractTargetValue(leftHand, mergeTarget) || []
            );
            for ( let i = 1; i < ids.length; i++ ) {
                const rightHandId = ids[i];
                const rightHand = rulesetMap.get(rightHandId);
                const rightHandArray =  extractTargetValue(rightHand, mergeTarget);
                if ( rightHandArray !== undefined ) {
                    if ( leftHandSet.size !== 0 ) {
                        for ( const item of rightHandArray ) {
                            leftHandSet.add(item);
                        }
                    }
                } else {
                    leftHandSet.clear();
                }
                rulesetMap.delete(rightHandId);
            }
            const leftHandOwner = extractTargetOwner(leftHand, mergeTarget);
            if ( leftHandSet.size > 1 ) {
                //if ( leftHandOwner === undefined ) { debugger; }
                leftHandOwner[mergeTarget] = Array.from(leftHandSet).sort();
            } else if ( leftHandSet.size === 0 ) {
                if ( leftHandOwner !== undefined ) {
                    leftHandOwner[mergeTarget] = undefined;
                }
            }
        }
    };
    mergeRules(rulesetMap, 'resourceTypes');
    mergeRules(rulesetMap, 'initiatorDomains');
    mergeRules(rulesetMap, 'requestDomains');
    mergeRules(rulesetMap, 'removeParams');
    mergeRules(rulesetMap, 'responseHeaders');

    // Patch id
    const rulesetFinal = [];
    {
        let ruleId = 1;
        for ( const rule of rulesetMap.values() ) {
            if ( rule._error === undefined ) {
                rule.id = ruleId++;
            } else {
                rule.id = 0;
            }
            rulesetFinal.push(rule);
        }
        for ( const invalid of context.invalid ) {
            rulesetFinal.push({ _error: [ invalid ] });
        }
    }

    network.ruleset = rulesetFinal;
}

/******************************************************************************/

async function dnrRulesetFromRawLists(lists, options = {}) {
    const context = Object.assign({}, options);
    staticNetFilteringEngine.dnrFromCompiled('begin', context);
    context.extensionPaths = new Map(context.extensionPaths || []);
    const toLoad = [];
    const toDNR = (context, list) => addToDNR(context, list);
    for ( const list of lists ) {
        if ( list instanceof Promise ) {
            toLoad.push(list.then(list => toDNR(context, list)));
        } else {
            toLoad.push(toDNR(context, list));
        }
    }
    await Promise.all(toLoad);
    const result = {
        network: staticNetFilteringEngine.dnrFromCompiled('end', context),
        genericCosmetic: context.genericCosmeticFilters,
        genericHighCosmetic: context.genericHighCosmeticFilters,
        genericCosmeticExceptions: context.genericCosmeticExceptions,
        specificCosmetic: context.specificCosmeticFilters,
        scriptlet: context.scriptletFilters,
    };
    if ( context.responseHeaderRules ) {
        result.network.ruleset.push(...context.responseHeaderRules);
    }
    finalizeRuleset(context, result.network);
    return result;
}

/******************************************************************************/

export { dnrRulesetFromRawLists };


/*******************************************************************************

    uBlock Origin - a comprehensive, efficient content blocker
    Copyright (C) 2016-present The uBlock Origin authors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

/* global browser, IDBDatabase, indexedDB */

'use strict';

/******************************************************************************/

import lz4Codec from './lz4.js';
import µb from './background.js';
import webext from './webext.js';

/******************************************************************************/

// The code below has been originally manually imported from:
// Commit: https://github.com/nikrolls/uBlock-Edge/commit/d1538ea9bea89d507219d3219592382eee306134
// Commit date: 29 October 2016
// Commit author: https://github.com/nikrolls
// Commit message: "Implement cacheStorage using IndexedDB"

// The original imported code has been subsequently modified as it was not
// compatible with Firefox.
// (a Promise thing, see https://github.com/dfahlander/Dexie.js/issues/317)
// Furthermore, code to migrate from browser.storage.local to vAPI.storage
// has been added, for seamless migration of cache-related entries into
// indexedDB.

// https://bugzilla.mozilla.org/show_bug.cgi?id=1371255
//   Firefox-specific: we use indexedDB because browser.storage.local() has
//   poor performance in Firefox.
// https://github.com/uBlockOrigin/uBlock-issues/issues/328
//   Use IndexedDB for Chromium as well, to take advantage of LZ4
//   compression.
// https://github.com/uBlockOrigin/uBlock-issues/issues/399
//   Revert Chromium support of IndexedDB, use advanced setting to force
//   IndexedDB.
// https://github.com/uBlockOrigin/uBlock-issues/issues/409
//   Allow forcing the use of webext storage on Firefox.

const STORAGE_NAME = 'uBlock0CacheStorage';

// Default to webext storage.
const storageLocal = webext.storage.local;

let storageReadyResolve;
const storageReadyPromise = new Promise(resolve => {
    storageReadyResolve = resolve;
});

const cacheStorage = {
    name: 'browser.storage.local',
    get(...args) {
        return storageReadyPromise.then(( ) =>
            storageLocal.get(...args).catch(reason => {
                console.log(reason);
            })
        );
    },
    set(...args) {
        return storageReadyPromise.then(( ) =>
            storageLocal.set(...args).catch(reason => {
                console.log(reason);
            })
        );
    },
    remove(...args) {
        return storageReadyPromise.then(( ) =>
            storageLocal.remove(...args).catch(reason => {
                console.log(reason);
            })
        );
    },
    clear(...args) {
        return storageReadyPromise.then(( ) =>
            storageLocal.clear(...args).catch(reason => {
                console.log(reason);
            })
        );
    },
    select: function(selectedBackend) {
        let actualBackend = selectedBackend;
        if ( actualBackend === undefined || actualBackend === 'unset' ) {
            actualBackend = vAPI.webextFlavor.soup.has('firefox')
                ? 'indexedDB'
                : 'browser.storage.local';
        }
        if ( actualBackend === 'indexedDB' ) {
            return selectIDB().then(success => {
                if ( success || selectedBackend === 'indexedDB' ) {
                    clearWebext();
                    storageReadyResolve();
                    return 'indexedDB';
                }
                clearIDB();
                storageReadyResolve();
                return 'browser.storage.local';
            });
        }
        if ( actualBackend === 'browser.storage.local' ) {
            clearIDB();
        }
        storageReadyResolve();
        return Promise.resolve('browser.storage.local');

    },
    error: undefined
};

// Not all platforms support getBytesInUse
if ( storageLocal.getBytesInUse instanceof Function ) {
    cacheStorage.getBytesInUse = function(...args) {
        return storageLocal.getBytesInUse(...args).catch(reason => {
            console.log(reason);
        });
    };
}

// Reassign API entries to that of indexedDB-based ones
const selectIDB = async function() {
    let db;
    let dbPromise;

    const noopfn = function () {
    };

    const disconnect = function() {
        dbTimer.off();
        if ( db instanceof IDBDatabase ) {
            db.close();
            db = undefined;
        }
    };

    const dbTimer = vAPI.defer.create(( ) => {
        disconnect();
    });

    const keepAlive = function() {
        dbTimer.offon(Math.max(
            µb.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000,
            180000
        ));
    };

    // https://github.com/gorhill/uBlock/issues/3156
    //   I have observed that no event was fired in Tor Browser 7.0.7 +
    //   medium security level after the request to open the database was
    //   created. When this occurs, I have also observed that the `error`
    //   property was already set, so this means uBO can detect here whether
    //   the database can be opened successfully. A try-catch block is
    //   necessary when reading the `error` property because we are not
    //   allowed to read this property outside of event handlers in newer
    //   implementation of IDBRequest (my understanding).

    const getDb = function() {
        keepAlive();
        if ( db !== undefined ) {
            return Promise.resolve(db);
        }
        if ( dbPromise !== undefined ) {
            return dbPromise;
        }
        dbPromise = new Promise(resolve => {
            let req;
            try {
                req = indexedDB.open(STORAGE_NAME, 1);
                if ( req.error ) {
                    console.log(req.error);
                    req = undefined;
                }
            } catch(ex) {
            }
            if ( req === undefined ) {
                db = null;
                dbPromise = undefined;
                return resolve(null);
            }
            req.onupgradeneeded = function(ev) {
                // https://github.com/uBlockOrigin/uBlock-issues/issues/2725
                //   If context Firefox + incognito mode, fall back to
                //   browser.storage.local for cache storage purpose.
                if (
                    vAPI.webextFlavor.soup.has('firefox') &&
                    browser.extension.inIncognitoContext === true
                ) {
                    return req.onerror();
                }
                if ( ev.oldVersion === 1 ) { return; }
                try {
                    const db = ev.target.result;
                    db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
                } catch(ex) {
                    req.onerror();
                }
            };
            req.onsuccess = function(ev) {
                if ( resolve === undefined ) { return; }
                req = undefined;
                db = ev.target.result;
                dbPromise = undefined;
                resolve(db);
                resolve = undefined;
            };
            req.onerror = req.onblocked = function() {
                if ( resolve === undefined ) { return; }
                req = undefined;
                console.log(this.error);
                db = null;
                dbPromise = undefined;
                resolve(null);
                resolve = undefined;
            };
            vAPI.defer.once(5000).then(( ) => {
                if ( resolve === undefined ) { return; }
                db = null;
                dbPromise = undefined;
                resolve(null);
                resolve = undefined;
            });
        });
        return dbPromise;
    };

    const fromBlob = function(data) {
        if ( data instanceof Blob === false ) {
            return Promise.resolve(data);
        }
        return new Promise(resolve => {
            const blobReader = new FileReader();
            blobReader.onloadend = ev => {
                resolve(new Uint8Array(ev.target.result));
            };
            blobReader.readAsArrayBuffer(data);
        });
    };

    const toBlob = function(data) {
        const value = data instanceof Uint8Array
            ? new Blob([ data ])
            : data;
        return Promise.resolve(value);
    };

    const compress = function(store, key, data) {
        return lz4Codec.encode(data, toBlob).then(value => {
            store.push({ key, value });
        });
    };

    const decompress = function(store, key, data) {
        return lz4Codec.decode(data, fromBlob).then(data => {
            store[key] = data;
        });
    };

    const getFromDb = async function(keys, keyvalStore, callback) {
        if ( typeof callback !== 'function' ) { return; }
        if ( keys.length === 0 ) { return callback(keyvalStore); }
        const promises = [];
        const gotOne = function() {
            if ( typeof this.result !== 'object' ) { return; }
            const { key, value } = this.result;
            keyvalStore[key] = value;
            if ( value instanceof Blob === false ) { return; }
            promises.push(decompress(keyvalStore, key, value));
        };
        try {
            const db = await getDb();
            if ( !db ) { return callback(); }
            const transaction = db.transaction(STORAGE_NAME, 'readonly');
            transaction.oncomplete =
            transaction.onerror =
            transaction.onabort = ( ) => {
                Promise.all(promises).then(( ) => {
                    callback(keyvalStore);
                });
            };
            const table = transaction.objectStore(STORAGE_NAME);
            for ( const key of keys ) {
                const req = table.get(key);
                req.onsuccess = gotOne;
                req.onerror = noopfn;
            }
        }
        catch(reason) {
            console.info(`cacheStorage.getFromDb() failed: ${reason}`);
            callback();
        }
    };

    const visitAllFromDb = async function(visitFn) {
        const db = await getDb();
        if ( !db ) { return visitFn(); }
        const transaction = db.transaction(STORAGE_NAME, 'readonly');
        transaction.oncomplete =
        transaction.onerror =
        transaction.onabort = ( ) => visitFn();
        const table = transaction.objectStore(STORAGE_NAME);
        const req = table.openCursor();
        req.onsuccess = function(ev) {
            let cursor = ev.target && ev.target.result;
            if ( !cursor ) { return; }
            let entry = cursor.value;
            visitFn(entry);
            cursor.continue();
        };
    };

    const getAllFromDb = function(callback) {
        if ( typeof callback !== 'function' ) { return; }
        const promises = [];
        const keyvalStore = {};
        visitAllFromDb(entry => {
            if ( entry === undefined ) {
                Promise.all(promises).then(( ) => {
                    callback(keyvalStore);
                });
                return;
            }
            const { key, value } = entry;
            keyvalStore[key] = value;
            if ( entry.value instanceof Blob === false ) { return; }
            promises.push(decompress(keyvalStore, key, value));
        }).catch(reason => {
            console.info(`cacheStorage.getAllFromDb() failed: ${reason}`);
            callback();
        });
    };

    // https://github.com/uBlockOrigin/uBlock-issues/issues/141
    //   Mind that IDBDatabase.transaction() and IDBObjectStore.put()
    //   can throw:
    //   https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/transaction
    //   https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/put

    const putToDb = async function(keyvalStore, callback) {
        if ( typeof callback !== 'function' ) {
            callback = noopfn;
        }
        const keys = Object.keys(keyvalStore);
        if ( keys.length === 0 ) { return callback(); }
        const promises = [ getDb() ];
        const entries = [];
        const dontCompress =
            µb.hiddenSettings.cacheStorageCompression !== true;
        for ( const key of keys ) {
            const value = keyvalStore[key];
            const isString = typeof value === 'string';
            if ( isString === false || dontCompress ) {
                entries.push({ key, value });
                continue;
            }
            promises.push(compress(entries, key, value));
        }
        const finish = ( ) => {
            if ( callback === undefined ) { return; }
            let cb = callback;
            callback = undefined;
            cb();
        };
        try {
            const results = await Promise.all(promises);
            const db = results[0];
            if ( !db ) { return callback(); }
            const transaction = db.transaction(
                STORAGE_NAME,
                'readwrite'
            );
            transaction.oncomplete =
            transaction.onerror =
            transaction.onabort = finish;
            const table = transaction.objectStore(STORAGE_NAME);
            for ( const entry of entries ) {
                table.put(entry);
            }
        } catch (ex) {
            finish();
        }
    };

    const deleteFromDb = async function(input, callback) {
        if ( typeof callback !== 'function' ) {
            callback = noopfn;
        }
        const keys = Array.isArray(input) ? input.slice() : [ input ];
        if ( keys.length === 0 ) { return callback(); }
        const finish = ( ) => {
            if ( callback === undefined ) { return; }
            let cb = callback;
            callback = undefined;
            cb();
        };
        try {
            const db = await getDb();
            if ( !db ) { return callback(); }
            const transaction = db.transaction(STORAGE_NAME, 'readwrite');
            transaction.oncomplete =
            transaction.onerror =
            transaction.onabort = finish;
            const table = transaction.objectStore(STORAGE_NAME);
            for ( const key of keys ) {
                table.delete(key);
            }
        } catch (ex) {
            finish();
        }
    };

    const clearDb = async function(callback) {
        if ( typeof callback !== 'function' ) {
            callback = noopfn;
        }
        try {
            const db = await getDb();
            if ( !db ) { return callback(); }
            const transaction = db.transaction(STORAGE_NAME, 'readwrite');
            transaction.oncomplete =
            transaction.onerror =
            transaction.onabort = ( ) => {
                callback();
            };
            transaction.objectStore(STORAGE_NAME).clear();
        }
        catch(reason) {
            console.info(`cacheStorage.clearDb() failed: ${reason}`);
            callback();
        }
    };

    await getDb();
    if ( !db ) { return false; }

    cacheStorage.name = 'indexedDB';
    cacheStorage.get = function get(keys) {
        return storageReadyPromise.then(( ) =>
            new Promise(resolve => {
                if ( keys === null ) {
                    return getAllFromDb(bin => resolve(bin));
                }
                let toRead, output = {};
                if ( typeof keys === 'string' ) {
                    toRead = [ keys ];
                } else if ( Array.isArray(keys) ) {
                    toRead = keys;
                } else /* if ( typeof keys === 'object' ) */ {
                    toRead = Object.keys(keys);
                    output = keys;
                }
                getFromDb(toRead, output, bin => resolve(bin));
            })
        );
    };
    cacheStorage.set = function set(keys) {
        return storageReadyPromise.then(( ) =>
            new Promise(resolve => {
                putToDb(keys, details => resolve(details));
            })
        );
    };
    cacheStorage.remove = function remove(keys) {
        return storageReadyPromise.then(( ) =>
            new Promise(resolve => {
                deleteFromDb(keys, ( ) => resolve());
            })
        );
    };
    cacheStorage.clear = function clear() {
        return storageReadyPromise.then(( ) =>
            new Promise(resolve => {
                clearDb(( ) => resolve());
            })
        );
    };
    cacheStorage.getBytesInUse = function getBytesInUse() {
        return Promise.resolve(0);
    };
    return true;
};

// https://github.com/uBlockOrigin/uBlock-issues/issues/328
//   Delete cache-related entries from webext storage.
const clearWebext = async function() {
    let bin;
    try {
        bin = await webext.storage.local.get('assetCacheRegistry');
    } catch(ex) {
        console.error(ex);
    }
    if ( bin instanceof Object === false ) { return; }
    if ( bin.assetCacheRegistry instanceof Object === false ) { return; }
    const toRemove = [
        'assetCacheRegistry',
        'assetSourceRegistry',
    ];
    for ( const key in bin.assetCacheRegistry ) {
        if ( bin.assetCacheRegistry.hasOwnProperty(key) ) {
            toRemove.push('cache/' + key);
        }
    }
    webext.storage.local.remove(toRemove);
};

const clearIDB = function() {
    try {
        indexedDB.deleteDatabase(STORAGE_NAME);
    } catch(ex) {
    }
};

/******************************************************************************/

export default cacheStorage;

/******************************************************************************/