Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds screenshot options to context menu and commands #27083

Open
wants to merge 51 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7decc5d
adds screenshot feature and flag
jonathansampson Dec 28, 2024
43206cd
adds context menu screenshot options
jonathansampson Dec 28, 2024
be49547
addresses flaky assumption in unit tests
jonathansampson Dec 28, 2024
0af8697
exposes functions to commander
jonathansampson Dec 28, 2024
0366f68
adds unit tests for context-menu/feature-flag
jonathansampson Dec 29, 2024
9033fcd
refactored into TabHelper pattern
jonathansampson Jan 2, 2025
eeed506
minor presubmit fixes
jonathansampson Jan 2, 2025
378d40a
removes unnecessary /tabs/ subdirectory
jonathansampson Jan 4, 2025
cfa83b7
removes unnecessary /ui/views/ subdirectory
jonathansampson Jan 4, 2025
19fc390
removes unnecessary /components/ directory
jonathansampson Jan 4, 2025
3477637
fixes dates and header guards
jonathansampson Jan 4, 2025
33b8bac
presubmit fixes
jonathansampson Jan 4, 2025
acf6d38
revert remnant changes to brave_ui_* files
jonathansampson Jan 4, 2025
9b43670
cleanup "command" remnants
jonathansampson Jan 4, 2025
7aa360c
more consistent naming
jonathansampson Jan 4, 2025
ac4b114
adopting more consistently-used pattern
jonathansampson Jan 4, 2025
ee798bf
provides an enabled-state method
jonathansampson Jan 4, 2025
c73fd66
presubmit cleanup
jonathansampson Jan 4, 2025
4a4767e
cleanup
jonathansampson Jan 4, 2025
e3a53fa
drop reliance on browser_finder
jonathansampson Jan 4, 2025
1d4e4ba
refactors into a tab feature
jonathansampson Jan 4, 2025
ec8b216
updates macro for feature inclusion
jonathansampson Jan 4, 2025
cbe723d
removes complex multi-line /* ... */ comment
jonathansampson Jan 5, 2025
fadc22d
conditionally enable the tab feature
jonathansampson Jan 5, 2025
fdeaefb
more accurate conditional method name
jonathansampson Jan 6, 2025
6e77f33
resolves omission of parameter names
jonathansampson Jan 6, 2025
4b5e0a4
improves screenshot utilities
jonathansampson Jan 6, 2025
2198da7
refactors to use browser reference
jonathansampson Jan 6, 2025
5b5682c
more accurate feature description
jonathansampson Jan 6, 2025
8197ef8
various simplifications
jonathansampson Jan 6, 2025
1b71d0e
moves screenshot strings to own file
jonathansampson Jan 6, 2025
6bf8a54
Don't show the submenu within devtools
jonathansampson Jan 6, 2025
50798f7
refactors devtools code into distinct helper
jonathansampson Jan 6, 2025
0f78167
pesubmit fixes
jonathansampson Jan 7, 2025
c86dabb
avoid forward declarations per style guide
jonathansampson Jan 7, 2025
a2887ec
Skip autocomplete classifier on empty selection
jonathansampson Jan 7, 2025
92f82e3
adds context-menu related unittests for screenshots
jonathansampson Jan 7, 2025
317462b
cleanup and simplify unit tests
jonathansampson Jan 7, 2025
748c3c6
dependency cleanup
jonathansampson Jan 7, 2025
fd5d3c3
moves to strategy-based screenshotting
jonathansampson Jan 9, 2025
bba8861
Simplifies clip-indicating member
jonathansampson Jan 10, 2025
f76198e
Update screenshots_tab_feature.cc
jonathansampson Jan 10, 2025
32d4f72
adds some comments around screenshot limitations
jonathansampson Jan 10, 2025
194be01
minor note changes
jonathansampson Jan 11, 2025
ee4b82e
removes unused includes
jonathansampson Jan 12, 2025
9ebdb08
clean up build.gn deps
jonathansampson Jan 13, 2025
01c9de6
uses explicit/intentional targets
jonathansampson Jan 13, 2025
6d39b1e
uses debug logs
jonathansampson Jan 14, 2025
9599a59
typo-correction to resolve build
jonathansampson Jan 16, 2025
3f4ba33
addressing minor feedback items
jonathansampson Jan 18, 2025
aecc9d0
adds GitHub Issue reference
jonathansampson Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/brave_command_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@

#define IDC_WINDOW_BRING_ALL_TABS 56320

// Screenshot commands
#define IDC_BRAVE_SCREENSHOT_TOOLS 56321
#define IDC_BRAVE_SCREENSHOTS_START_SELECTION_TO_CLIPBOARD 56322
#define IDC_BRAVE_SCREENSHOTS_START_VIEWPORT_TO_CLIPBOARD 56323
#define IDC_BRAVE_SCREENSHOTS_START_FULLPAGE_TO_CLIPBOARD 56324

// Commands related to split view
#define IDC_NEW_SPLIT_VIEW 56325
#define IDC_TILE_TABS 56326
Expand Down
4 changes: 3 additions & 1 deletion app/brave_generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@
<messages fallback_to_english="true">
<!-- Brave Settings WebUI strings -->
<part file="brave_settings_strings.grdp" />
<!-- Brave Screenshots context-menu/settings strings -->
<part file="brave_screenshots_strings.grdp" />

<!--Add new items to the appropriate sections below -->

Expand Down Expand Up @@ -346,7 +348,7 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
Check details
</message>

<!-- Adblock contenxt menu -->
<!-- Adblock context menu -->
<message name="IDS_ADBLOCK_CONTEXT_BLOCK_ELEMENTS" desc="Message for context menu that enables the element picker UI to apply cosmetic filter rules to the page">
Block elements
</message>
Expand Down
31 changes: 31 additions & 0 deletions app/brave_screenshots_strings.grdp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version='1.0' encoding='UTF-8'?>
<grit-part>
<if expr="use_titlecase">
<message name="IDS_IDC_BRAVE_SCREENSHOT_TOOLS" desc="Title for the screenshot submenu">
Screenshot Tools
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_SELECTION_TO_CLIPBOARD" desc="Title for option to screenshot a region and copy to clipboard">
Screenshot Selection to Clipboard
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_VIEWPORT_TO_CLIPBOARD" desc="Title for option to screenshot the viewport and copy to clipboard">
Screenshot Viewport to Clipboard
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_FULLPAGE_TO_CLIPBOARD" desc="Title for option to screenshot the full page and copy to clipboard">
Screenshot Full Page to Clipboard
</message>
</if>
<if expr="not use_titlecase">
<message name="IDS_IDC_BRAVE_SCREENSHOT_TOOLS" desc="Title for the screenshot submenu">
Screenshot tools
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_SELECTION_TO_CLIPBOARD" desc="Title for option to screenshot a region and copy to clipboard">
Screenshot selection to clipboard
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_VIEWPORT_TO_CLIPBOARD" desc="Title for option to screenshot the viewport and copy to clipboard">
Screenshot viewport to clipboard
</message>
<message name="IDS_IDC_BRAVE_SCREENSHOTS_START_FULLPAGE_TO_CLIPBOARD" desc="Title for option to screenshot the full page and copy to clipboard">
Screenshot full page to clipboard
</message>
</if>
</grit-part>
8 changes: 8 additions & 0 deletions browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "base/strings/string_util.h"
#include "brave/browser/brave_browser_features.h"
#include "brave/browser/brave_features_internal_names.h"
#include "brave/browser/brave_screenshots/features.h"
#include "brave/browser/ethereum_remote_client/buildflags/buildflags.h"
#include "brave/browser/ethereum_remote_client/features.h"
#include "brave/browser/ui/brave_ui_features.h"
Expand Down Expand Up @@ -501,6 +502,13 @@
kOsDesktop, \
FEATURE_VALUE_TYPE(features::kBraveNtpSearchWidget), \
}, \
{ \
"brave-screenshots", \
"Screenshot Options", \
"Enables screenshot support via browser commands and context menu.", \
kOsDesktop, \
FEATURE_VALUE_TYPE(brave_screenshots::features::kBraveScreenshots), \
}, \
{ \
"brave-adblock-cname-uncloaking", \
"Enable CNAME uncloaking", \
Expand Down
74 changes: 74 additions & 0 deletions browser/brave_screenshots/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright (c) 2025 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("//brave/browser/brave_screenshots/buildflags/buildflags.gni")

assert(enable_brave_screenshots)

source_set("utils") {
sources = [
"screenshots_utils.cc",
"screenshots_utils.h",
]

deps = [
"//chrome/browser/image_editor:image_editor",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be just //chrome/browser/image_editor same as "//chrome/browser/ui and //ui/base/clipboard

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I purposefully provide the target names, just to more clearly communicate intent. If it's okay, I'd prefer to keep them explicit.

"//chrome/browser/ui:ui",
"//chrome/browser/ui/browser_window:browser_window",
"//ui/base/clipboard:clipboard",
]
}

source_set("strategies") {
sources = [
"strategies/fullpage_strategy.cc",
"strategies/fullpage_strategy.h",
"strategies/screenshot_strategy.h",
"strategies/selection_strategy.cc",
"strategies/selection_strategy.h",
"strategies/viewport_strategy.cc",
"strategies/viewport_strategy.h",
]

deps = [
"//chrome/browser/image_editor:image_editor",
"//chrome/browser/ui:ui",
]
}

source_set("tabs") {
sources = [
"screenshots_tab_feature.cc",
"screenshots_tab_feature.h",
]

deps = [
":strategies",
":utils",
"//chrome/browser/image_editor:image_editor",
"//chrome/browser/ui:ui",
]
}

source_set("features") {
sources = [
"features.cc",
"features.h",
]

deps = [ "//base:base" ]
}

source_set("unit_tests") {
testonly = true
sources = [ "test/brave_screenshots_unittests.cc" ]
deps = [
":features",
"//base:base",
"//brave/app:command_ids",
"//chrome/test:test_support",
"//testing/gtest:gtest",
]
}
8 changes: 8 additions & 0 deletions browser/brave_screenshots/buildflags/buildflags.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2025 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/.

declare_args() {
enable_brave_screenshots = is_win || is_mac || is_linux
}
17 changes: 17 additions & 0 deletions browser/brave_screenshots/features.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2025 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/.

#include "brave/browser/brave_screenshots/features.h"

namespace brave_screenshots::features {

BASE_FEATURE(kBraveScreenshots,
"BraveScreenshots",
base::FEATURE_ENABLED_BY_DEFAULT);

bool IsBraveScreenshotsEnabled() {
return base::FeatureList::IsEnabled(kBraveScreenshots);
}
} // namespace brave_screenshots::features
17 changes: 17 additions & 0 deletions browser/brave_screenshots/features.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2025 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/.

#ifndef BRAVE_BROWSER_BRAVE_SCREENSHOTS_FEATURES_H_
#define BRAVE_BROWSER_BRAVE_SCREENSHOTS_FEATURES_H_

#include "base/feature_list.h"

namespace brave_screenshots::features {
BASE_DECLARE_FEATURE(kBraveScreenshots);

bool IsBraveScreenshotsEnabled();
} // namespace brave_screenshots::features

#endif // BRAVE_BROWSER_BRAVE_SCREENSHOTS_FEATURES_H_
96 changes: 96 additions & 0 deletions browser/brave_screenshots/screenshots_tab_feature.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) 2025 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/.

#include "brave/browser/brave_screenshots/screenshots_tab_feature.h"

#include "base/notreached.h"
#include "brave/browser/brave_screenshots/screenshots_utils.h"
#include "brave/browser/brave_screenshots/strategies/fullpage_strategy.h"
#include "brave/browser/brave_screenshots/strategies/selection_strategy.h"
#include "brave/browser/brave_screenshots/strategies/viewport_strategy.h"
#include "chrome/browser/image_editor/screenshot_flow.h"
#include "chrome/browser/ui/browser.h"
#include "content/public/browser/web_contents.h"

namespace {

// Some screenshots may need to be clipped to avoid the GPU limit.
// See https://crbug.com/1260828. When this happens, we may wish to notify the
// user that only a portion of their page could be captured.
void DisplayScreenshotClippedNotification(base::WeakPtr<Browser> browser) {
// Issue: https://github.com/brave/brave-browser/issues/43369
NOTIMPLEMENTED();
}

} // namespace
namespace brave_screenshots {

BraveScreenshotsTabFeature::BraveScreenshotsTabFeature() {
DVLOG(1) << "BraveScreenshotsTabFeature created";
}

BraveScreenshotsTabFeature::~BraveScreenshotsTabFeature() {
DVLOG(1) << "BraveScreenshotsTabFeature destroyed";
}

void BraveScreenshotsTabFeature::StartScreenshot(Browser* browser,
ScreenshotType type) {
DVLOG(1) << "Called StartScreenshot";
CHECK(browser);

browser_ = browser->AsWeakPtr();
web_contents_ =
browser_->tab_strip_model()->GetActiveWebContents()->GetWeakPtr();

// We've determined the appropriate strategy to use
strategy_ = CreateStrategy(type);

DVLOG(2) << "Starting capture";

strategy_->Capture(
web_contents_.get(),
base::BindOnce(&BraveScreenshotsTabFeature::OnCaptureComplete,
weak_factory_.GetWeakPtr()));
}

std::unique_ptr<BraveScreenshotStrategy>
BraveScreenshotsTabFeature::CreateStrategy(ScreenshotType type) {
switch (type) {
case ScreenshotType::kFullPage:
DVLOG(3) << "Creating FullPageStrategy";
return std::make_unique<FullPageStrategy>();
case ScreenshotType::kSelection:
// Based on image_editor::ScreenshotFlow, which requires a WebContents
DVLOG(3) << "Creating SelectionStrategy";
return std::make_unique<SelectionStrategy>(web_contents_.get());
case ScreenshotType::kViewport:
// Based on image_editor::ScreenshotFlow, which requires a WebContents
DVLOG(3) << "Creating ViewportStrategy";
return std::make_unique<ViewportStrategy>(web_contents_.get());
default:
NOTREACHED();
}
}

void BraveScreenshotsTabFeature::OnCaptureComplete(
const image_editor::ScreenshotCaptureResult& result) {
DVLOG(2) << __func__;

if (result.image.IsEmpty()) {
DVLOG(2) << "Screenshot capture failed";
return;
}

if (strategy_->DidClipScreenshot()) {
DisplayScreenshotClippedNotification(browser_);
}

if (browser_) {
utils::CopyImageToClipboard(result);
utils::DisplayScreenshotBubble(result, browser_);
}
}

} // namespace brave_screenshots
47 changes: 47 additions & 0 deletions browser/brave_screenshots/screenshots_tab_feature.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2025 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/.

#ifndef BRAVE_BROWSER_BRAVE_SCREENSHOTS_SCREENSHOTS_TAB_FEATURE_H_
#define BRAVE_BROWSER_BRAVE_SCREENSHOTS_SCREENSHOTS_TAB_FEATURE_H_

#include <memory>

#include "base/memory/weak_ptr.h"
#include "brave/browser/brave_screenshots/strategies/screenshot_strategy.h"
#include "chrome/browser/image_editor/screenshot_flow.h"
#include "chrome/browser/ui/browser.h"
#include "content/public/browser/web_contents.h"

namespace brave_screenshots {

enum ScreenshotType {
kSelection,
kViewport,
kFullPage,
};

class BraveScreenshotsTabFeature {
public:
BraveScreenshotsTabFeature();
BraveScreenshotsTabFeature(const BraveScreenshotsTabFeature&) = delete;
BraveScreenshotsTabFeature& operator=(const BraveScreenshotsTabFeature&) =
delete;
~BraveScreenshotsTabFeature();

void StartScreenshot(Browser* browser, ScreenshotType type);

private:
void OnCaptureComplete(const image_editor::ScreenshotCaptureResult& result);
std::unique_ptr<BraveScreenshotStrategy> CreateStrategy(ScreenshotType type);
base::WeakPtr<Browser> browser_ = nullptr;
std::unique_ptr<BraveScreenshotStrategy> strategy_ = nullptr;
base::WeakPtr<content::WebContents> web_contents_ = nullptr;
base::WeakPtrFactory<BraveScreenshotsTabFeature> weak_factory_{this};

}; // class BraveScreenshotsTabFeature

} // namespace brave_screenshots

#endif // BRAVE_BROWSER_BRAVE_SCREENSHOTS_SCREENSHOTS_TAB_FEATURE_H_
44 changes: 44 additions & 0 deletions browser/brave_screenshots/screenshots_utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2025 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/.

#include "brave/browser/brave_screenshots/screenshots_utils.h"

#include "chrome/browser/image_editor/screenshot_flow.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"

namespace brave_screenshots::utils {

using image_editor::ScreenshotCaptureResult;

void CopyImageToClipboard(const ScreenshotCaptureResult& result) {
DVLOG(2) << __func__;
if (result.image.IsEmpty()) {
DVLOG(2) << "Image is empty";
return;
}

DVLOG(2) << "Writing image to clipboard";
// Copy the image to the user's clipboard
ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
.WriteImage(*result.image.ToSkBitmap());
}

void DisplayScreenshotBubble(const ScreenshotCaptureResult& result,
base::WeakPtr<Browser> browser) {
DVLOG(2) << __func__;
if (!browser || result.image.IsEmpty()) {
DVLOG(2) << "Browser is null or image is empty";
return;
}

DVLOG(2) << "Displaying screenshot bubble";
// Leverage the screenshot bubble to show the user the screenshot
browser->window()->ShowScreenshotCapturedBubble(
browser->tab_strip_model()->GetActiveWebContents(), result.image);
}

} // namespace brave_screenshots::utils
Loading
Loading