Skip to content

Commit

Permalink
Added Custom Scriptlet section in the settings page.
Browse files Browse the repository at this point in the history
  • Loading branch information
boocmp committed Oct 15, 2024
1 parent 74bf470 commit 9b318d9
Show file tree
Hide file tree
Showing 24 changed files with 891 additions and 25 deletions.
36 changes: 36 additions & 0 deletions app/brave_settings_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,42 @@
<message name="IDS_BRAVE_ADBLOCK_FILTER_LISTS_INPUT_PLACEHOLDER" desc="A placeholder label for the input form to allow the user to filter from a list of content filters">
Filter lists
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLETS_LIST_LABEL" desc="Label for custom scriptlets lists section">
Custom scriptlets
</message>
<message name="IDS_BRAVE_ADBLOCK_ADD_CUSTOM_SCRIPTLET_BUTTON" desc="A label for the add custom scriptlet button">
Add new scriptlet
</message>
<message name="IDS_BRAVE_ADBLOCK_ADD_CUSTOM_SCRIPTLET_DIALOG_TITLE" desc="A title for the add custom scriptlet dialog">
Add new scriptlet
</message>
<message name="IDS_BRAVE_ADBLOCK_EDIT_CUSTOM_SCRIPTLET_DIALOG_TITLE" desc="A title for the edit custom scriptlet dialog">
Edit scriptlet
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_NAME_LABEL" desc="A label for the custom scriptlet name field">
Name
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_CONTENT_LABEL" desc="A label for the custom scriptlet content field">
Content
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_CANCEL_BUTTON" desc="A label for the custom scriptlet content cancel button">
Cancel
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_SAVE_BUTTON" desc="A label for the custom scriptlet content save button">
Save
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DELETE_CONFIRMATION" desc="A confirmation message for the custom scriptlet deletion">
Are you sure you want to delete this scriptlet? If this scriptlet is used in any custom filter, it will no longer work.
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_ALREADY_EXISTS_ERROR" desc="Error message">
Scriptlet is already exists.
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_INVALID_NAME_ERROR" desc="Error message">
Invalid scriptlet name.
</message>
<message name="IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_NOT_FOUND_ERROR" desc="Error message">
Scriptlet is not found.
</message>

<message name="IDS_SETTINGS_BRAVE_SHORTCUTS_TITLE" desc="The text label for the shortcuts settings page">
Shortcuts
Expand Down
4 changes: 1 addition & 3 deletions browser/brave_shields/ad_block_service_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,7 @@ void AdBlockServiceTest::UpdateAdBlockResources(const std::string& resources) {
brave_shields::AdBlockService* service =
g_brave_browser_process->ad_block_service();

static_cast<brave_shields::AdBlockDefaultResourceProvider*>(
service->resource_provider())
->OnComponentReady(component_path);
service->default_resource_provider()->OnComponentReady(component_path);
}

void AdBlockServiceTest::UpdateAdBlockInstanceWithRules(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@

// @ts-nocheck TODO(petemill): Define types and remove ts-nocheck

import { sendWithPromise, addWebUiListener } from 'chrome://resources/js/cr.js';
import { sendWithPromise, addWebUiListener } from 'chrome://resources/js/cr.js'

export class Scriptlet {
name: string
kind: object = {
mime: 'application/javascript'
}
content: string
}

export enum ErrorCode {
kOK = 0,
kInvalidName,
kAlreadyExists,
kNotFound,
}

export interface BraveAdblockBrowserProxy {
getRegionalLists(): Promise<any[]> // TODO(petemill): Define the expected type
Expand All @@ -19,6 +34,10 @@ export interface BraveAdblockBrowserProxy {
updateSubscription(url: string)
deleteSubscription(url: string)
viewSubscription(url: string)
getCustomScriptlets(): Promise<Scriptlet[]>
addCustomScriptlet(scriptlet: Scriptlet): Promise<ErrorCode>
updateCustomScriptlet(name: string, scriptlet: Scriptlet): Promise<ErrorCode>
removeCustomScriptlet(name: string): Promise<ErrorCode>
}

export class BraveAdblockBrowserProxyImpl implements BraveAdblockBrowserProxy {
Expand Down Expand Up @@ -74,7 +93,53 @@ export class BraveAdblockBrowserProxyImpl implements BraveAdblockBrowserProxy {
chrome.send('brave_adblock.viewSubscription', [url])
}

addWebUiListener (event_name, callback) {
utf8ToBase64_(str) {
const uint8Array = new TextEncoder().encode(str)
const base64String = btoa(String.fromCharCode.apply(null, uint8Array))
return base64String
}

base64ToUtf8_(base64) {
const binaryString = atob(base64)
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
return new TextDecoder().decode(bytes)
}

getCustomScriptlets() {
return sendWithPromise('brave_adblock.getCustomScriptlets')
.then((scriptlets) => {
for (const scriptlet of scriptlets) {
scriptlet.content = this.base64ToUtf8_(scriptlet.content)
}
return scriptlets
})
.catch((error) => {
throw error
})
}

addCustomScriptlet(scriptlet) {
scriptlet.content = this.utf8ToBase64_(scriptlet.content)
return sendWithPromise('brave_adblock.addCustomScriptlet', scriptlet)
}

updateCustomScriptlet(name, scriptlet) {
scriptlet.content = this.utf8ToBase64_(scriptlet.content)
return sendWithPromise(
'brave_adblock.updateCustomScriptlet',
name,
scriptlet
)
}

removeCustomScriptlet(name) {
return sendWithPromise('brave_adblock.removeCustomScriptlet', name)
}

addWebUiListener(event_name, callback) {
addWebUiListener(event_name, callback)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,13 @@
></adblock-filter-editor>
</div>
</div>
<template is="dom-if" if="[[cosmeticFilteringCustomScriptletsEnabled_]]">
<div class="settings-box">
<div class="flex">
<div class="label shields-primary-title">
$i18n{adblockCustomSciptletsListLabel}
</div>
<adblock-scriptlet-list></adblock-scriptlet-list>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {BaseMixin} from '../base_mixin.js';
import {BraveAdblockBrowserProxyImpl} from './brave_adblock_browser_proxy.js'
import {getTemplate} from './brave_adblock_subpage.html.js'

import { loadTimeData } from '../i18n_setup.js'

const AdBlockSubpageBase = PrefsMixin(I18nMixin(BaseMixin(PolymerElement)))

class AdBlockSubpage extends AdBlockSubpageBase {
Expand All @@ -41,6 +43,12 @@ class AdBlockSubpage extends AdBlockSubpageBase {
type: Boolean,
value: false
},
cosmeticFilteringCustomScriptletsEnabled_: {
type: Boolean,
value: loadTimeData.getBoolean(
'cosmeticFilteringCustomScriptletsEnabled'
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<style include="settings-shared md-select">
div[slot=body] {
overflow: hidden;
}

.flex {
display: grid;
grid-template-columns: 1fr 70%;
align-items: baseline;
}

.content {
margin-bottom: 5px;
}

textarea {
white-space: pre;
overflow-wrap: normal;
overflow-x: scroll;
width: 100%;
min-height: 350px;
max-height: 700px;
resize: vertical;
box-sizing: border-box;
}
</style>

<cr-dialog id="dialog" show-close-button show-on-attach>
<div slot="title">[[dialogTitle_]]</div>
<div slot="body">
<div class="flex">
<div>$i18n{adblockCustomSciptletDialogNameLabel}</div>
<cr-input id="scriptlet-name" value="{{scriptlet.name}}" invalid="[[!isScriptletValid_]]" autofocus required
spellcheck="false" on-input="validateName_" error-message="[[scriptletErrorMessage_]]">
</cr-input>
</div>
<div>
<div class="flex content">$i18n{adblockCustomScriptletDialogContentLabel}</div>
<textarea id="scriptlet-content" multiline="true" value="{{scriptlet.content::input}}" spellcheck="false">
</textarea>
</div>
</div>
<div slot="button-container">
<cr-button class="cancel-button" id="cancel"
on-click="cancelClicked_">$i18n{adblockCustomScriptletDialogCancelButton}
</cr-button>
<cr-button class="action-button" id="save" on-click="saveClicked_" disabled="[[!isScriptletValid_]]">
$i18n{adblockCustomScriptletDialogSaveButton}
</cr-button>
</div>
</cr-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

import 'chrome://resources/cr_elements/cr_button/cr_button.js'
import { CrDialogElement } from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'
import 'chrome://resources/cr_elements/cr_input/cr_input.js'

import { PrefsMixin } from '/shared/settings/prefs/prefs_mixin.js'
import { I18nMixin } from 'chrome://resources/cr_elements/i18n_mixin.js'
import { PolymerElement } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'

import { getTemplate } from './brave_adblock_scriptlet_editor.html.js'

import {
Scriptlet,
BraveAdblockBrowserProxyImpl,
ErrorCode
} from '../brave_adblock_browser_proxy.js'

interface AdblockScriptletEditor {
$: {
dialog: CrDialogElement
}
}

const AdblockScriptletEditorBase = I18nMixin(PrefsMixin(PolymerElement))

class AdblockScriptletEditor extends AdblockScriptletEditorBase {
static get is() {
return 'adblock-scriptlet-editor'
}

static get template() {
return getTemplate()
}

static get properties() {
return {
scriptlet: Scriptlet,
dialogTitle_: String,
isScriptletValid_: Boolean,
scriptletErrorMessage_: String
}
}

scriptlet: Scriptlet
dialogTitle_: string
isScriptletValid_: boolean
scriptletErrorMessage_: string

scriptletName_: string
browserProxy_ = BraveAdblockBrowserProxyImpl.getInstance()

override ready() {
super.ready()
this.scriptletName_ = this.scriptlet.name

if (this.scriptletName_) {
this.dialogTitle_ = this.i18n('adblockEditCustomScriptletDialogTitle')
} else {
this.dialogTitle_ = this.i18n('adblockAddCustomScriptletDialogTitle')
}

this.updateError(ErrorCode.kOK)
}

updateError(error_code: ErrorCode) {
this.isScriptletValid_ = error_code === ErrorCode.kOK
switch (error_code) {
case ErrorCode.kOK:
this.scriptletErrorMessage_ = ''
break
case ErrorCode.kAlreadyExists:
this.scriptletErrorMessage_ = this.i18n(
'adblockEditCustomScriptletAlreadyExistsError'
)
break
case ErrorCode.kInvalidName:
this.scriptletErrorMessage_ = this.i18n(
'adblockEditCustomScriptletInvalidNameError'
)
break
case ErrorCode.kNotFound:
this.scriptletErrorMessage_ = this.i18n(
'adblockEditCustomScriptletNotFoundError'
)
break
}
}

cancelClicked_() {
this.$.dialog.cancel()
}

saveClicked_() {
if (this.scriptletName_) {
this.browserProxy_
.updateCustomScriptlet(this.scriptletName_, this.scriptlet)
.then((e) => {
this.updateError(e)
if (this.isScriptletValid_) {
this.$.dialog.close()
}
})
} else {
this.browserProxy_.addCustomScriptlet(this.scriptlet).then((e) => {
this.updateError(e)
if (this.isScriptletValid_) {
this.$.dialog.close()
}
})
}
}

validateName_() {
if (!/^[a-z-_.]*$/.test(this.scriptlet.name)) {
this.updateError(ErrorCode.kInvalidName)
} else {
this.updateError(ErrorCode.kOK)
}
}
}

customElements.define(AdblockScriptletEditor.is, AdblockScriptletEditor)
Loading

0 comments on commit 9b318d9

Please sign in to comment.