Repository: brave/brave-core
Pull request #28268: [cr135 follow up][Settings] Restores theme link behavior.
Author: login: mkarolin
Labels: name: CI/storybook-url
description: Deploy storybook and provide a unique URL for each build
Created: 2025-03-22T02:14:12Z | Merged: 2025-03-25T04:31:51Z

<!-- Add brave-browser issue below that this PR will resolve -->
Resolves https://github.com/brave/brave-browser/issues/44821

<!-- CI-related labels that can be applied to this PR:
* CI/run-audit-deps (1) - check for known npm/cargo vulnerabilities (audit_deps)
* CI/run-network-audit (1) - run network-audit
* CI/run-upstream-tests - run Chromium unit and browser tests on Linux and Windows (otherwise only on Linux)
* CI/run-linux-arm64, CI/run-macos-x64, CI/run-windows-arm64, CI/run-windows-x86 - run builds that would otherwise be skipped
* CI/skip - do not run CI builds (except noplatform)
* CI/skip-linux-x64, CI/skip-android, CI/skip-macos-arm64, CI/skip-ios, CI/skip-windows-x64 - skip CI builds for specific platforms
* CI/skip-upstream-tests - do not run Chromium unit, or browser tests (otherwise only on Linux)
* CI/skip-all-linters - do not run presubmit and lint checks
* CI/storybook-url (1) - deploy storybook and provide a unique URL for each build

(1) applied automatically when some files are changed (see: https://github.com/brave/brave-core/blob/master/.github/labeler.yml)
-->

## Submitter Checklist:

- [ ] I confirm that no [security/privacy review is needed](https://github.com/brave/brave-browser/wiki/Security-reviews) and no other type of reviews are needed, or that I have [requested](https://github.com/brave/reviews/issues/new/choose) them
- [ ] There is a [ticket](https://github.com/brave/brave-browser/issues) for my issue
- [ ] Used Github [auto-closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in the PR description above
- [ ] Wrote a good [PR/commit description](https://google.github.io/eng-practices/review/developer/cl-descriptions.html)
- [ ] Squashed any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
- [ ] Added appropriate labels (`QA/Yes` or `QA/No`; `release-notes/include` or `release-notes/exclude`; `OS/...`) to the associated issue
- [ ] Checked the PR locally:
  * `npm run test -- brave_browser_tests`, `npm run test -- brave_unit_tests` [wiki](https://github.com/brave/brave-browser/wiki/Tests)
  * `npm run presubmit` [wiki](https://github.com/brave/brave-browser/wiki/Presubmit-checks), `npm run gn_check`, `npm run tslint`
- [ ] Ran `git rebase master` (if needed)

## Reviewer Checklist:

- [ ] A security review [is not needed](https://github.com/brave/brave-browser/wiki/Security-reviews), or a link to one is included in the PR description
- [ ] New files have MPL-2.0 license header
- [ ] Adequate test coverage exists to prevent regressions
- [ ] Major classes, functions and non-trivial code blocks are well-commented
- [ ] Changes in component dependencies are properly reflected in `gn`
- [ ] Code follows the [style guide](https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++.md)
- [ ] Test plan is specified in PR before merging

## After-merge Checklist:

- [ ] The associated issue milestone is set to the smallest version that the
  changes has landed on
- [ ] All relevant documentation has been updated, for instance:
  - [ ] https://github.com/brave/brave-browser/wiki/Deviations-from-Chromium-(features-we-disable-or-remove)
  - [ ] https://github.com/brave/brave-browser/wiki/Proxy-redirected-URLs
  - [ ] https://github.com/brave/brave-browser/wiki/Fingerprinting-Protections
  - [ ] https://github.com/brave/brave-browser/wiki/Brave%E2%80%99s-Use-of-Referral-Codes
  - [ ] https://github.com/brave/brave-browser/wiki/Web-Compatibility-Exceptions-in-Brave
  - [ ] https://github.com/brave/brave-browser/wiki/QA-Guide
  - [ ] https://github.com/brave/brave-browser/wiki/P3A

## Test Plan:




Diff:
diff --git a/app/brave_settings_strings.grdp b/app/brave_settings_strings.grdp
index 35899eca14b6..b58653b889a7 100644
--- a/app/brave_settings_strings.grdp
+++ b/app/brave_settings_strings.grdp
@@ -189,6 +189,12 @@
       Always show toolbar in full screen
     </message>
   </if>
+  <message name="IDS_SETTINGS_APPEARANCE_SETTINGS_THEMES_GALLERY_URL" translateable="false">
+    https://chrome.google.com/webstore?hl=[GRITLANGCODE]&amp;category=theme
+  </message>
+  <message name="IDS_SETTINGS_APPEARANCE_SETTINGS_OPEN_WEB_STORE" desc="Sub-label about choosing something from the Chrome Web Store.">
+    Open Web Store
+  </message>
   <message name="IDS_SETTINGS_APPEARANCE_SETTINGS_GET_MORE_THEMES" desc="The label for opening theme web store">
     Get more themes
   </message>
diff --git a/browser/resources/settings/brave_overrides/appearance_page.ts b/browser/resources/settings/brave_overrides/appearance_page.ts
index 41e7c7664823..8dd3c6939017 100644
--- a/browser/resources/settings/brave_overrides/appearance_page.ts
+++ b/browser/resources/settings/brave_overrides/appearance_page.ts
@@ -6,12 +6,22 @@
 import '../brave_appearance_page/super_referral.js'
 import '../brave_appearance_page/brave_theme.js'
 
-import {html, RegisterPolymerTemplateModifications} from 'chrome://resources/brave/polymer_overriding.js'
+import {
+  html,
+  RegisterPolymerComponentReplacement,
+  RegisterPolymerTemplateModifications
+} from 'chrome://resources/brave/polymer_overriding.js'
+
 import {getTrustedHTML} from 'chrome://resources/js/static_types.js'
 
 import {loadTimeData} from '../i18n_setup.js'
 import {Router} from '../router.js'
 
+import {
+  SettingsAppearancePageElement
+} from '../appearance_page/appearance_page.js'
+
+
 const superReferralStringId = 'superReferralThemeName'
 
 RegisterPolymerTemplateModifications({
@@ -20,6 +30,26 @@ RegisterPolymerTemplateModifications({
     if (!theme) {
       console.error(`[Settings] Couldn't find #themeRow`)
     } else {
+      // Insert Brave colors dropdown before theme.
+      theme.setAttribute('class', 'settings-row hr')
+      theme.insertAdjacentHTML(
+        'beforebegin',
+        getTrustedHTML`
+          <settings-brave-appearance-theme prefs="{{prefs}}">
+          </settings-brave-appearance-theme>
+      `)
+      const openTheme = templateContent.getElementById('openTheme')
+      if (!openTheme) {
+        console.error(`[Settings] Couldn't find #openTheme`)
+      } else {
+        // Remove upstream's click handler. We add our own in
+        // BraveSettingsAppearancePageElement below.
+        openTheme.removeAttribute('on-click')
+        // Restore sub-label to say Open Web Store when no theme is set.
+        openTheme.setAttribute('sub-label',
+          '[[_adjustThemeSublabel(themeSublabel_)]]')
+      }
+      // Fix theme "Reset to default" button.
       const useDefaultButtonTemplate = templateContent.querySelector(
         'template[is=dom-if][if="[[prefs.extensions.theme.id.value]]"]')
       if (!useDefaultButtonTemplate) {
@@ -29,13 +59,6 @@ RegisterPolymerTemplateModifications({
       } else {
         useDefaultButtonTemplate.setAttribute('restamp', 'true')
       }
-      theme.setAttribute('class', 'settings-row hr')
-      theme.insertAdjacentHTML(
-        'beforebegin',
-        getTrustedHTML`
-          <settings-brave-appearance-theme prefs="{{prefs}}">
-          </settings-brave-appearance-theme>
-        `)
     }
 
     const customizeToolbar = templateContent.getElementById('customizeToolbar')
@@ -213,3 +236,38 @@ RegisterPolymerTemplateModifications({
     }
   }
 })
+
+RegisterPolymerComponentReplacement(
+  'settings-appearance-page',
+  class BraveSettingsAppearancePageElement
+    extends SettingsAppearancePageElement {
+    override ready() {
+      super.ready()
+      const openTheme = this.shadowRoot!.getElementById('openTheme')
+      if (!openTheme) {
+        console.error(`[Settings] Couldn't find #openTheme`)
+      } else {
+        // Restore theme link to point to web store instead of to the profile
+        // color picker.
+        openTheme.addEventListener('click',
+          () => {
+            window.open((this as any).themeUrl_ ||
+            loadTimeData.getString('appearanceSettingsThemesGalleryUrl'),
+            '_blank', 'noopener noreferrer');
+          }
+        )
+      }
+    }
+
+    // Restore theme sub-label to say "Open Web Store" when there's no custom
+    // theme set.
+    _adjustThemeSublabel(themeSublabel: string) {
+      // <if expr="not is_linux">
+      if (themeSublabel === '') {
+        return loadTimeData.getString('appearanceSettingsOpenWebStore')
+      }
+      // </if>
+      return themeSublabel
+    }
+  }
+)
diff --git a/browser/resources/settings/brave_overrides/config.ts b/browser/resources/settings/brave_overrides/config.ts
index 2f79545bc787..a7c77b8f436f 100644
--- a/browser/resources/settings/brave_overrides/config.ts
+++ b/browser/resources/settings/brave_overrides/config.ts
@@ -15,6 +15,7 @@ import {RegisterPolymerComponentToIgnore} from 'chrome://resources/brave/polymer
 // component before the module is imported).
 
 RegisterPolymerComponentToIgnore('add-site-dialog')
+RegisterPolymerComponentToIgnore('settings-appearance-page')
 RegisterPolymerComponentToIgnore('settings-autofill-page')
 RegisterPolymerComponentToIgnore('settings-clear-browsing-data-dialog')
 RegisterPolymerComponentToIgnore('settings-search-page')
diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
index bbfdb6b190af..f070076970e5 100644
--- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
+++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
@@ -227,6 +227,10 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source,
        IDS_SETTINGS_SITE_SETTINGS_SHIELDS_DESCRIPTION},
       {"appearanceSettingsBraveTheme",
        IDS_SETTINGS_APPEARANCE_SETTINGS_BRAVE_THEMES},
+      {"appearanceSettingsThemesGalleryUrl",
+       IDS_SETTINGS_APPEARANCE_SETTINGS_THEMES_GALLERY_URL},
+      {"appearanceSettingsOpenWebStore",
+       IDS_SETTINGS_APPEARANCE_SETTINGS_OPEN_WEB_STORE},
       {"appearanceSettingsShowBookmarksButton",
        IDS_SETTINGS_APPEARANCE_SETTINGS_SHOW_BOOKMARKS_BUTTON},
       {"appearanceSettingsLocationBarIsWide",


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-22T02:20:28Z
user: login: emerick
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-22T11:22:15Z
user: login: mkarolin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-22T20:17:28Z
user: login: mkarolin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-22T20:18:07Z
user: login: petemill
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-25T04:28:58Z

Review comments:
diff_hunk: @@ -213,3 +236,37 @@ RegisterPolymerTemplateModifications({
     }
   }
 })
+
+RegisterPolymerComponentReplacement(
+  'settings-appearance-page',
+  class BraveSettingsAppearancePageElement
+    extends SettingsAppearancePageElement {
+    override ready() {
+      super.ready()
+      const openTheme = this.shadowRoot!.getElementById('openTheme')
+      if (!openTheme) {
+        console.error(`[Settings] Couldn't find #openTheme`)
+      } else {
+        // Restore theme link to point to web store instead of to the profile
+        // color picker.
+        openTheme.addEventListener('click',
+          () => {
+            window.open((this as any).themeUrl_ ||
+            loadTimeData.getString('appearanceSettingsThemesGalleryUrl'));
path: browser/resources/settings/brave_overrides/appearance_page.ts
user: login: github-actions[bot]
body: <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[semgrep] window.open should enforce `noopener` to avoid tab-nabbing vulnerabilities.<br><br>Source: https://github.com/brave/security-action/blob/main/assets/semgrep_rules/services/missing-noopener-window-open-native.yaml<br><br><!-- Category: security --><br>Cc @thypon @kdenhartog 
<!-- __reviewdog__:ChBkYzQxMmFlYWYzYWUwNDhm -->

created_at: 2025-03-22T02:20:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -213,3 +236,37 @@ RegisterPolymerTemplateModifications({
     }
   }
 })
+
+RegisterPolymerComponentReplacement(
+  'settings-appearance-page',
+  class BraveSettingsAppearancePageElement
+    extends SettingsAppearancePageElement {
+    override ready() {
+      super.ready()
+      const openTheme = this.shadowRoot!.getElementById('openTheme')
+      if (!openTheme) {
+        console.error(`[Settings] Couldn't find #openTheme`)
+      } else {
+        // Restore theme link to point to web store instead of to the profile
+        // color picker.
+        openTheme.addEventListener('click',
+          () => {
+            window.open((this as any).themeUrl_ ||
+            loadTimeData.getString('appearanceSettingsThemesGalleryUrl'));
path: browser/resources/settings/brave_overrides/appearance_page.ts
user: login: mkarolin
body: Added 'noopener noreferrer'
created_at: 2025-03-22T20:17:28Z
author_association: COLLABORATOR
diff_hunk: @@ -213,3 +236,37 @@ RegisterPolymerTemplateModifications({
     }
   }
 })
+
+RegisterPolymerComponentReplacement(
+  'settings-appearance-page',
+  class BraveSettingsAppearancePageElement
+    extends SettingsAppearancePageElement {
+    override ready() {
+      super.ready()
+      const openTheme = this.shadowRoot!.getElementById('openTheme')
+      if (!openTheme) {
+        console.error(`[Settings] Couldn't find #openTheme`)
+      } else {
+        // Restore theme link to point to web store instead of to the profile
+        // color picker.
+        openTheme.addEventListener('click',
+          () => {
+            window.open((this as any).themeUrl_ ||
+            loadTimeData.getString('appearanceSettingsThemesGalleryUrl'));
path: browser/resources/settings/brave_overrides/appearance_page.ts
user: login: mkarolin
body: Removed needs-security-review.
created_at: 2025-03-22T20:18:07Z
author_association: COLLABORATOR

PR comments:
user: login: brave-builds
created_at: 2025-03-22T03:10:38Z
body: A Storybook has been deployed to [preview UI for the latest push](https://build-artifacts.brave.com/brave-browser-build-pr/maxk-fix-theme-link/storybook.html)
author_association: COLLABORATOR
user: login: brave-builds
created_at: 2025-03-25T12:03:13Z
body: Released in [v1.78.64](https://github.com/brave/brave-browser/releases/tag/v1.78.64)
author_association: COLLABORATOR