Skip to content

Commit

Permalink
[GM3][Panorama] Connect Color Scheme Mode Selector to ThemeService
Browse files Browse the repository at this point in the history
* Create page handler for CustomizeColorSchemeMode that can set the
  color scheme and get the color scheme whenever theme is changed. There
  is also a method for initializing to force a SetColorSchemeMode to be
  sent to the client.
* Create unit tests for the page handler.
* Connect the component to the page handler.
* Create browser tests for the component.

Context: This is for a UI button that allows the user to set light/dark
mode for the browser (or system mode to follow the OS setting). The
handler is calling a ThemeService method that sets a pref and notifies
observers that there has been a theme change and another one that gets
the current value of that same pref.

Bug: 1445804
Change-Id: Iaeda2f4c25167b967bf2c2fd009835b663c1c613
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4582248
Reviewed-by: John Lee <johntlee@chromium.org>
Reviewed-by: Tibor Goldschwendt <tiborg@chromium.org>
Commit-Queue: Riley Tatum <rtatum@google.com>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Code-Coverage: Findit <findit-for-me@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#1153700}
  • Loading branch information
Riley Tatum authored and Chromium LUCI CQ committed Jun 6, 2023
1 parent a39f599 commit 41f429d
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 7 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,8 @@ static_library("ui") {
"webui/commerce/shopping_list_context_menu_controller.h",
"webui/commerce/shopping_ui_handler_delegate.cc",
"webui/commerce/shopping_ui_handler_delegate.h",
"webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.cc",
"webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h",
"webui/cr_components/history_clusters/history_clusters_util.cc",
"webui/cr_components/history_clusters/history_clusters_util.h",
"webui/cr_components/most_visited/most_visited_handler.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file://chrome/browser/ui/webui/new_tab_page/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h"

#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"

CustomizeColorSchemeModeHandler::CustomizeColorSchemeModeHandler(
mojo::PendingRemote<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
pending_client,
mojo::PendingReceiver<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
pending_handler,
Profile* profile)
: remote_client_(std::move(pending_client)),
receiver_(this, std::move(pending_handler)),
theme_service_(ThemeServiceFactory::GetForProfile(profile)) {
CHECK(theme_service_);
theme_service_->AddObserver(this);
}

CustomizeColorSchemeModeHandler::~CustomizeColorSchemeModeHandler() {
theme_service_->RemoveObserver(this);
}

void CustomizeColorSchemeModeHandler::SetColorSchemeMode(
customize_color_scheme_mode::mojom::ColorSchemeMode colorMode) {
theme_service_->SetBrowserColorScheme(
static_cast<ThemeService::BrowserColorScheme>(colorMode));
}

void CustomizeColorSchemeModeHandler::InitializeColorSchemeMode() {
CustomizeColorSchemeModeHandler::UpdateColorSchemeMode();
}

void CustomizeColorSchemeModeHandler::OnThemeChanged() {
CustomizeColorSchemeModeHandler::UpdateColorSchemeMode();
}

void CustomizeColorSchemeModeHandler::UpdateColorSchemeMode() {
remote_client_->SetColorSchemeMode(
static_cast<customize_color_scheme_mode::mojom::ColorSchemeMode>(
theme_service_->GetBrowserColorScheme()));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_CUSTOMIZE_COLOR_SCHEME_MODE_CUSTOMIZE_COLOR_SCHEME_MODE_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_CUSTOMIZE_COLOR_SCHEME_MODE_CUSTOMIZE_COLOR_SCHEME_MODE_HANDLER_H_

#include "base/memory/raw_ptr.h"
#include "chrome/browser/themes/theme_service_observer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"

class Profile;
class ThemeService;

class CustomizeColorSchemeModeHandler
: public customize_color_scheme_mode::mojom::
CustomizeColorSchemeModeHandler,
public ThemeServiceObserver {
public:
explicit CustomizeColorSchemeModeHandler(
mojo::PendingRemote<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
pending_client,
mojo::PendingReceiver<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
pending_handler,
Profile* profile);
~CustomizeColorSchemeModeHandler() override;

// customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler:
void SetColorSchemeMode(
customize_color_scheme_mode::mojom::ColorSchemeMode colorMode) override;
void InitializeColorSchemeMode() override;

// ThemeServiceObserver:
void OnThemeChanged() override;

private:
void UpdateColorSchemeMode();

mojo::Remote<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
remote_client_;
mojo::Receiver<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeHandler>
receiver_;

const raw_ptr<ThemeService> theme_service_;
};

#endif // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_CUSTOMIZE_COLOR_SCHEME_MODE_CUSTOMIZE_COLOR_SCHEME_MODE_HANDLER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler.h"

#include "base/memory/raw_ptr.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_contents_factory.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/webui/resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom.h"

namespace content {
class BrowserContext;
} // namespace content

namespace {

using testing::_;

class MockClient : public customize_color_scheme_mode::mojom::
CustomizeColorSchemeModeClient {
public:
MockClient() = default;
~MockClient() override = default;

mojo::PendingRemote<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
BindAndGetRemote() {
DCHECK(!receiver_.is_bound());
return receiver_.BindNewPipeAndPassRemote();
}

void FlushForTesting() { receiver_.FlushForTesting(); }

MOCK_METHOD(void,
SetColorSchemeMode,
(customize_color_scheme_mode::mojom::ColorSchemeMode));

mojo::Receiver<
customize_color_scheme_mode::mojom::CustomizeColorSchemeModeClient>
receiver_{this};
};

class MockThemeService : public ThemeService {
public:
MockThemeService() : ThemeService(nullptr, theme_helper_) { set_ready(); }
MOCK_CONST_METHOD0(GetBrowserColorScheme, ThemeService::BrowserColorScheme());
MOCK_METHOD1(SetBrowserColorScheme, void(ThemeService::BrowserColorScheme));
MOCK_METHOD1(AddObserver, void(ThemeServiceObserver*));

private:
ThemeHelper theme_helper_;
};

std::unique_ptr<TestingProfile> MakeTestingProfile() {
TestingProfile::Builder profile_builder;
profile_builder.AddTestingFactory(
ThemeServiceFactory::GetInstance(),
base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<testing::NiceMock<MockThemeService>>();
}));
auto profile = profile_builder.Build();
return profile;
}
} // namespace

class CustomizeColorSchemeModeHandlerTest : public testing::Test {
public:
CustomizeColorSchemeModeHandlerTest()
: profile_(MakeTestingProfile()),
mock_theme_service_(static_cast<MockThemeService*>(
ThemeServiceFactory::GetForProfile(profile_.get()))) {}

void SetUp() override {
EXPECT_CALL(mock_theme_service(), AddObserver).Times(1);
handler_ = std::make_unique<CustomizeColorSchemeModeHandler>(
mock_client_.BindAndGetRemote(),
mojo::PendingReceiver<customize_color_scheme_mode::mojom::
CustomizeColorSchemeModeHandler>(),
profile_.get());
mock_client_.FlushForTesting();
}

TestingProfile& profile() { return *profile_; }
CustomizeColorSchemeModeHandler& handler() { return *handler_; }
MockThemeService& mock_theme_service() { return *mock_theme_service_; }

protected:
testing::NiceMock<MockClient> mock_client_;
// NOTE: The initialization order of these members matters.
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfile> profile_;
raw_ptr<MockThemeService> mock_theme_service_;
std::unique_ptr<CustomizeColorSchemeModeHandler> handler_;
};

TEST_F(CustomizeColorSchemeModeHandlerTest, InitializeColorSchemeMode) {
ON_CALL(mock_theme_service(), GetBrowserColorScheme)
.WillByDefault(testing::Return(ThemeService::BrowserColorScheme::kLight));

customize_color_scheme_mode::mojom::ColorSchemeMode color_scheme_mode;
EXPECT_CALL(mock_client_, SetColorSchemeMode)
.Times(1)
.WillOnce(testing::Invoke(
[&color_scheme_mode](
customize_color_scheme_mode::mojom::ColorSchemeMode arg) {
color_scheme_mode = std::move(arg);
}));

handler().InitializeColorSchemeMode();
mock_client_.FlushForTesting();

EXPECT_EQ(color_scheme_mode,
customize_color_scheme_mode::mojom::ColorSchemeMode::kLight);
}

TEST_F(CustomizeColorSchemeModeHandlerTest, OnThemeChanged) {
ON_CALL(mock_theme_service(), GetBrowserColorScheme)
.WillByDefault(testing::Return(ThemeService::BrowserColorScheme::kLight));

customize_color_scheme_mode::mojom::ColorSchemeMode color_scheme_mode;
EXPECT_CALL(mock_client_, SetColorSchemeMode)
.Times(1)
.WillOnce(testing::Invoke(
[&color_scheme_mode](
customize_color_scheme_mode::mojom::ColorSchemeMode arg) {
color_scheme_mode = std::move(arg);
}));

handler().OnThemeChanged();
mock_client_.FlushForTesting();

EXPECT_EQ(color_scheme_mode,
customize_color_scheme_mode::mojom::ColorSchemeMode::kLight);
}

TEST_F(CustomizeColorSchemeModeHandlerTest, SetColorSchemeMode) {
ThemeService::BrowserColorScheme color_scheme_mode;
EXPECT_CALL(mock_theme_service(), SetBrowserColorScheme)
.Times(1)
.WillOnce(testing::Invoke(
[&color_scheme_mode](ThemeService::BrowserColorScheme arg) {
color_scheme_mode = std::move(arg);
}));

handler().SetColorSchemeMode(
customize_color_scheme_mode::mojom::ColorSchemeMode::kLight);
mock_client_.FlushForTesting();

EXPECT_EQ(color_scheme_mode, ThemeService::BrowserColorScheme::kLight);
}
1 change: 1 addition & 0 deletions chrome/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -7325,6 +7325,7 @@ test("unit_tests") {
"../browser/ui/webui/commerce/shopping_list_context_menu_controller_unittest.cc",
"../browser/ui/webui/components/components_handler_unittest.cc",
"../browser/ui/webui/constrained_web_dialog_ui_unittest.cc",
"../browser/ui/webui/cr_components/customize_color_scheme_mode/customize_color_scheme_mode_handler_unittest.cc",
"../browser/ui/webui/customize_themes/chrome_customize_themes_handler_unittest.cc",
"../browser/ui/webui/devtools_ui_data_source_unittest.cc",
"../browser/ui/webui/discards/graph_dump_impl_unittest.cc",
Expand Down
2 changes: 2 additions & 0 deletions chrome/test/data/webui/cr_components/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ build_webui_tests("build") {
"app_management/window_mode_item_test.ts",
"app_management/uninstall_button_test.ts",
"color_change_listener_test.ts",
"customize_color_scheme_mode_test.ts",
"customize_themes_test.ts",
"help_bubble_mixin_test.ts",
"help_bubble_test.ts",
Expand Down Expand Up @@ -44,6 +45,7 @@ build_webui_tests("build") {
"//third_party/polymer/v3_0:library",
"//ui/webui/resources/cr_components/app_management:build_ts",
"//ui/webui/resources/cr_components/color_change_listener:build_ts",
"//ui/webui/resources/cr_components/customize_color_scheme_mode:build_ts",
"//ui/webui/resources/cr_components/customize_themes:build_ts",
"//ui/webui/resources/cr_components/help_bubble:build_ts",
"//ui/webui/resources/cr_components/history_clusters:build_ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ var CrComponentsMojoBrowserTest = class extends PolymerTest {
}
};

var CrComponentsCustomizeColorSchemeModeTest =
class extends CrComponentsMojoBrowserTest {
/** @override */
get browsePreload() {
return 'chrome://new-tab-page/test_loader.html?module=cr_components/customize_color_scheme_mode_test.js';
}
};

TEST_F('CrComponentsCustomizeColorSchemeModeTest', 'All', function() {
mocha.run();
});

var CrComponentsCustomizeThemesTest =
class extends CrComponentsMojoBrowserTest {
/** @override */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'chrome://webui-test/mojo_webui_test_support.js';

import {CustomizeColorSchemeModeBrowserProxy} from 'chrome://resources/cr_components/customize_color_scheme_mode/browser_proxy.js';
import {ColorSchemeModeOption, colorSchemeModeOptions, CustomizeColorSchemeModeElement} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.js';
import {ColorSchemeMode, CustomizeColorSchemeModeClientCallbackRouter, CustomizeColorSchemeModeClientRemote, CustomizeColorSchemeModeHandlerRemote} from 'chrome://resources/cr_components/customize_color_scheme_mode/customize_color_scheme_mode.mojom-webui.js';
import {assertEquals} from 'chrome://webui-test/chai_assert.js';
import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
import {TestMock} from 'chrome://webui-test/test_mock.js';

suite('CrComponentsCustomizeColorSchemeModeTest', () => {
let handler: TestMock<CustomizeColorSchemeModeHandlerRemote>&
CustomizeColorSchemeModeHandlerRemote;
let callbackRouter: CustomizeColorSchemeModeClientRemote;

setup(() => {
document.body.innerHTML = window.trustedTypes!.emptyHTML;
handler = TestMock.fromClass(CustomizeColorSchemeModeHandlerRemote);
CustomizeColorSchemeModeBrowserProxy.setInstance(
handler, new CustomizeColorSchemeModeClientCallbackRouter());
callbackRouter = CustomizeColorSchemeModeBrowserProxy.getInstance()
.callbackRouter.$.bindNewPipeAndPassRemote();
});

async function initializeElement(colorSchemeMode: ColorSchemeMode):
Promise<CustomizeColorSchemeModeElement> {
const element = new CustomizeColorSchemeModeElement();
callbackRouter.setColorSchemeMode(colorSchemeMode);
document.body.appendChild(element);
await waitAfterNextRender(element);
return element;
}

colorSchemeModeOptions.forEach((mode: ColorSchemeModeOption) => {
test(`Set ${mode.name} on initialization`, async () => {
// Arrange.
const element = await initializeElement(mode.value);

// Assert.
// Check that the correct option is checked and the other aren't.
colorSchemeModeOptions.forEach((innerMode: ColorSchemeModeOption) => {
const checked = innerMode.id === mode.id;
assertEquals(
element.shadowRoot!
.querySelector(`cr-segmented-button-option[name="${
innerMode.id}"]`)!.hasAttribute('checked'),
checked);
});
});

test(`Click ${mode.name} sets scheme mode`, async () => {
// Arrange.
const element = await initializeElement(mode.value);

// Action.
const option =
element.shadowRoot!.querySelector(
`cr-segmented-button-option[name="${mode.id}"]`)! as HTMLElement;
option.click();

// Assert.
assertEquals(1, handler.getCallCount('setColorSchemeMode'));
});
});
});
1 change: 1 addition & 0 deletions tools/typescript/path_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def _add_ui_webui_resources_mappings(path_mappings, root_gen_dir):
"cr_components/app_management",
"cr_components/certificate_manager",
"cr_components/color_change_listener",
"cr_components/customize_color_scheme_mode",
"cr_components/customize_themes",
"cr_components/help_bubble",
"cr_components/history_clusters",
Expand Down

0 comments on commit 41f429d

Please sign in to comment.