Commit f987fe47 authored by Ionel Popescu's avatar Ionel Popescu Committed by Commit Bot

Add Mac support for the eye dropper.

This CL adds support for the eye dropper on Mac by using NSColorSampler.
This approach avoids the security dialog asking for more permissions.

Bug: 992297
Change-Id: Ia70d6c4daf3ec678e558ca528b63bfe960363f0f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378773Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Commit-Queue: Ionel Popescu <iopopesc@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#802457}
parent 666cec61
......@@ -210,6 +210,7 @@
#endif // OS_CHROMEOS
#if defined(OS_MAC)
#include "base/mac/mac_util.h"
#include "chrome/browser/ui/browser_dialogs.h"
#endif // OS_MAC
......@@ -5364,7 +5365,7 @@ const FeatureEntry kFeatureEntries[] = {
FEATURE_VALUE_TYPE(features::kFormControlsRefresh)},
{"color-picker-eye-dropper", flag_descriptions::kColorPickerEyeDropperName,
flag_descriptions::kColorPickerEyeDropperDescription, kOsWin,
flag_descriptions::kColorPickerEyeDropperDescription, kOsWin | kOsMac,
FEATURE_VALUE_TYPE(features::kEyeDropper)},
#if defined(OS_CHROMEOS)
......@@ -6526,6 +6527,15 @@ bool SkipConditionalFeatureEntry(const flags_ui::FlagsStorage* storage,
}
#endif // OS_ANDROID
#if defined(OS_MAC)
// The Eye Dropper relies on the NSColorSampler API, which is available
// starting with macOS 10.15.
if (!strcmp("color-picker-eye-dropper", entry.internal_name) &&
!base::mac::IsAtLeastOS10_15()) {
return true;
}
#endif // OS_MAC
if (flags::IsFlagExpired(storage, entry.internal_name))
return true;
......
......@@ -1287,6 +1287,8 @@ static_library("ui") {
"unload_controller.h",
"views/eye_dropper/eye_dropper.cc",
"views/eye_dropper/eye_dropper.h",
"views/eye_dropper/eye_dropper_view_mac.h",
"views/eye_dropper/eye_dropper_view_mac.mm",
"webui/app_launcher_login_handler.cc",
"webui/app_launcher_login_handler.h",
"webui/app_management/app_management_page_handler.cc",
......@@ -3023,8 +3025,6 @@ static_library("ui") {
"views/color_chooser_win.cc",
"views/critical_notification_bubble_view.cc",
"views/critical_notification_bubble_view.h",
"views/eye_dropper/eye_dropper_win.cc",
"views/eye_dropper/eye_dropper_win.h",
"views/frame/browser_desktop_window_tree_host.h",
"views/frame/browser_desktop_window_tree_host_win.cc",
"views/frame/browser_desktop_window_tree_host_win.h",
......@@ -4159,6 +4159,8 @@ static_library("ui") {
"views/apps/shaped_app_window_targeter.cc",
"views/apps/shaped_app_window_targeter.h",
"views/dropdown_bar_host_aura.cc",
"views/eye_dropper/eye_dropper_view_aura.cc",
"views/eye_dropper/eye_dropper_view_aura.h",
"views/renderer_context_menu/render_view_context_menu_views.cc",
"views/renderer_context_menu/render_view_context_menu_views.h",
"views/tab_contents/chrome_web_contents_view_delegate_views.cc",
......
......@@ -7,11 +7,11 @@
#include "build/build_config.h"
#include "content/public/browser/eye_dropper.h"
#if !defined(OS_WIN)
#if !defined(USE_AURA) && !defined(OS_MAC)
// Used for the platforms that don't support an eye dropper.
std::unique_ptr<content::EyeDropper> ShowEyeDropper(
content::RenderFrameHost* frame,
content::EyeDropperListener* listener) {
return nullptr;
}
#endif // !defined(OS_WIN)
#endif // !defined(USE_AURA) && !defined(OS_MAC)
......@@ -18,7 +18,7 @@
#include "ui/display/display_switches.h"
#if defined(OS_WIN)
#include "chrome/browser/ui/views/eye_dropper/eye_dropper_win.h"
#include "chrome/browser/ui/views/eye_dropper/eye_dropper_view_aura.h"
#endif
class EyeDropperBrowserTest : public UiBrowserTest,
......@@ -33,9 +33,11 @@ class EyeDropperBrowserTest : public UiBrowserTest,
// UiBrowserTest:
void ShowUi(const std::string& name) override {
#if defined(OS_WIN)
content::RenderFrameHost* parent_frame =
browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
eye_dropper_ = ShowEyeDropper(parent_frame, /*listener=*/nullptr);
#endif
}
bool VerifyUi() override {
......@@ -44,7 +46,7 @@ class EyeDropperBrowserTest : public UiBrowserTest,
return false;
views::Widget* widget =
static_cast<EyeDropperWin*>(eye_dropper_.get())->GetWidget();
static_cast<EyeDropperViewAura*>(eye_dropper_.get())->GetWidget();
auto* test_info = testing::UnitTest::GetInstance()->current_test_info();
const std::string screenshot_name =
base::StrCat({test_info->test_case_name(), "_", test_info->name()});
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/eye_dropper/eye_dropper_win.h"
#include "chrome/browser/ui/views/eye_dropper/eye_dropper_view_aura.h"
#include "base/time/time.h"
#include "chrome/browser/ui/views/eye_dropper/eye_dropper.h"
......@@ -19,9 +19,9 @@
#include "ui/gfx/native_widget_types.h"
#include "ui/views/widget/widget.h"
class EyeDropperWin::PreEventDispatchHandler : public ui::EventHandler {
class EyeDropperViewAura::PreEventDispatchHandler : public ui::EventHandler {
public:
explicit PreEventDispatchHandler(aura::Window* owner, EyeDropperWin* view);
explicit PreEventDispatchHandler(EyeDropperViewAura* view);
PreEventDispatchHandler(const PreEventDispatchHandler&) = delete;
PreEventDispatchHandler& operator=(const PreEventDispatchHandler&) = delete;
~PreEventDispatchHandler() override;
......@@ -29,35 +29,37 @@ class EyeDropperWin::PreEventDispatchHandler : public ui::EventHandler {
private:
void OnMouseEvent(ui::MouseEvent* event) override;
aura::Window* owner_;
EyeDropperWin* view_;
EyeDropperViewAura* view_;
};
EyeDropperWin::PreEventDispatchHandler::PreEventDispatchHandler(
aura::Window* owner,
EyeDropperWin* view)
: owner_(owner), view_(view) {
EyeDropperViewAura::PreEventDispatchHandler::PreEventDispatchHandler(
EyeDropperViewAura* view)
: view_(view) {
// Ensure that this handler is called before color popup handler by using
// a higher priority.
owner_->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem);
view->GetWidget()->GetNativeWindow()->AddPreTargetHandler(
this, ui::EventTarget::Priority::kSystem);
}
EyeDropperWin::PreEventDispatchHandler::~PreEventDispatchHandler() {
owner_->RemovePreTargetHandler(this);
EyeDropperViewAura::PreEventDispatchHandler::~PreEventDispatchHandler() {
view_->GetWidget()->GetNativeWindow()->RemovePreTargetHandler(this);
}
void EyeDropperWin::PreEventDispatchHandler::OnMouseEvent(
void EyeDropperViewAura::PreEventDispatchHandler::OnMouseEvent(
ui::MouseEvent* event) {
if (event->type() == ui::ET_MOUSE_PRESSED) {
// Avoid closing the color popup when the eye dropper is clicked.
event->StopPropagation();
}
if (event->type() == ui::ET_MOUSE_RELEASED) {
view_->OnColorSelected();
event->StopPropagation();
}
}
class EyeDropperWin::ViewPositionHandler {
class EyeDropperViewAura::ViewPositionHandler {
public:
explicit ViewPositionHandler(EyeDropperWin* owner);
explicit ViewPositionHandler(EyeDropperViewAura* owner);
ViewPositionHandler(const ViewPositionHandler&) = delete;
ViewPositionHandler& operator=(const ViewPositionHandler&) = delete;
~ViewPositionHandler();
......@@ -68,27 +70,29 @@ class EyeDropperWin::ViewPositionHandler {
// Timer used for updating the window location.
base::RepeatingTimer timer_;
EyeDropperWin* owner_;
EyeDropperViewAura* owner_;
};
EyeDropperWin::ViewPositionHandler::ViewPositionHandler(EyeDropperWin* owner)
EyeDropperViewAura::ViewPositionHandler::ViewPositionHandler(
EyeDropperViewAura* owner)
: owner_(owner) {
// Use a value close to the refresh rate @60hz.
// TODO(iopopesc): Use SetCapture instead of a timer when support for
// activating the eye dropper without closing the color popup is added.
timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(16), this,
&EyeDropperWin::ViewPositionHandler::UpdateViewPosition);
&EyeDropperViewAura::ViewPositionHandler::UpdateViewPosition);
}
EyeDropperWin::ViewPositionHandler::~ViewPositionHandler() {
EyeDropperViewAura::ViewPositionHandler::~ViewPositionHandler() {
timer_.AbandonAndStop();
}
void EyeDropperWin::ViewPositionHandler::UpdateViewPosition() {
void EyeDropperViewAura::ViewPositionHandler::UpdateViewPosition() {
owner_->UpdatePosition();
}
class EyeDropperWin::ScreenCapturer : public webrtc::DesktopCapturer::Callback {
class EyeDropperViewAura::ScreenCapturer
: public webrtc::DesktopCapturer::Callback {
public:
ScreenCapturer();
ScreenCapturer(const ScreenCapturer&) = delete;
......@@ -106,7 +110,7 @@ class EyeDropperWin::ScreenCapturer : public webrtc::DesktopCapturer::Callback {
SkBitmap frame_;
};
EyeDropperWin::ScreenCapturer::ScreenCapturer() {
EyeDropperViewAura::ScreenCapturer::ScreenCapturer() {
// TODO(iopopesc): Update the captured frame after a period of time to match
// latest content on screen.
capturer_ = content::desktop_capture::CreateScreenCapturer();
......@@ -114,7 +118,7 @@ EyeDropperWin::ScreenCapturer::ScreenCapturer() {
capturer_->CaptureFrame();
}
void EyeDropperWin::ScreenCapturer::OnCaptureResult(
void EyeDropperViewAura::ScreenCapturer::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) {
if (result != webrtc::DesktopCapturer::Result::SUCCESS)
......@@ -126,11 +130,11 @@ void EyeDropperWin::ScreenCapturer::OnCaptureResult(
frame_.setImmutable();
}
SkBitmap EyeDropperWin::ScreenCapturer::GetBitmap() const {
SkBitmap EyeDropperViewAura::ScreenCapturer::GetBitmap() const {
return frame_;
}
EyeDropperWin::EyeDropperWin(content::RenderFrameHost* frame,
EyeDropperViewAura::EyeDropperViewAura(content::RenderFrameHost* frame,
content::EyeDropperListener* listener)
: render_frame_host_(frame),
listener_(listener),
......@@ -154,8 +158,7 @@ EyeDropperWin::EyeDropperWin(content::RenderFrameHost* frame,
widget->Init(std::move(params));
widget->SetContentsView(this);
pre_dispatch_handler_ = std::make_unique<PreEventDispatchHandler>(
widget->GetNativeWindow(), this);
pre_dispatch_handler_ = std::make_unique<PreEventDispatchHandler>(this);
auto* cursor_client =
aura::client::GetCursorClient(widget->GetNativeWindow()->GetRootWindow());
......@@ -164,21 +167,21 @@ EyeDropperWin::EyeDropperWin(content::RenderFrameHost* frame,
widget->Show();
}
EyeDropperWin::~EyeDropperWin() {
EyeDropperViewAura::~EyeDropperViewAura() {
if (GetWidget())
GetWidget()->CloseNow();
}
void EyeDropperWin::OnPaint(gfx::Canvas* view_canvas) {
void EyeDropperViewAura::OnPaint(gfx::Canvas* view_canvas) {
if (screen_capturer_->GetBitmap().drawsNothing())
return;
constexpr float kDiameter = 90;
constexpr float kPixelSize = 10;
// Clip circle for magnified projection.
const gfx::Size padding((size().width() - kDiameter) / 2,
(size().height() - kDiameter) / 2);
// Clip circle for magnified projection.
SkPath clip_path;
clip_path.addOval(SkRect::MakeXYWH(padding.width(), padding.height(),
kDiameter, kDiameter));
......@@ -240,23 +243,23 @@ void EyeDropperWin::OnPaint(gfx::Canvas* view_canvas) {
OnPaintBorder(view_canvas);
}
void EyeDropperWin::WindowClosing() {
void EyeDropperViewAura::WindowClosing() {
aura::client::GetCursorClient(GetWidget()->GetNativeWindow()->GetRootWindow())
->UnlockCursor();
pre_dispatch_handler_.reset();
}
ui::ModalType EyeDropperWin::GetModalType() const {
ui::ModalType EyeDropperViewAura::GetModalType() const {
return ui::MODAL_TYPE_SYSTEM;
}
void EyeDropperWin::OnWidgetMove() {
void EyeDropperViewAura::OnWidgetMove() {
// Trigger a repaint since because the widget was moved, the content of the
// view needs to be updated.
SchedulePaint();
}
void EyeDropperWin::UpdatePosition() {
void EyeDropperViewAura::UpdatePosition() {
if (screen_capturer_->GetBitmap().drawsNothing() || !GetWidget())
return;
......@@ -271,7 +274,7 @@ void EyeDropperWin::UpdatePosition() {
size()));
}
void EyeDropperWin::OnColorSelected() {
void EyeDropperViewAura::OnColorSelected() {
if (!selected_color_.has_value()) {
listener_->ColorSelectionCanceled();
return;
......@@ -284,5 +287,5 @@ void EyeDropperWin::OnColorSelected() {
std::unique_ptr<content::EyeDropper> ShowEyeDropper(
content::RenderFrameHost* frame,
content::EyeDropperListener* listener) {
return std::make_unique<EyeDropperWin>(frame, listener);
return std::make_unique<EyeDropperViewAura>(frame, listener);
}
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_WIN_H_
#define CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_WIN_H_
#ifndef CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_AURA_H_
#define CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_AURA_H_
#include <memory>
......@@ -15,14 +15,14 @@
#include "ui/gfx/geometry/point.h"
#include "ui/views/widget/widget_delegate.h"
class EyeDropperWin : public content::EyeDropper,
class EyeDropperViewAura : public content::EyeDropper,
public views::WidgetDelegateView {
public:
EyeDropperWin(content::RenderFrameHost* frame,
EyeDropperViewAura(content::RenderFrameHost* frame,
content::EyeDropperListener* listener);
EyeDropperWin(const EyeDropperWin&) = delete;
EyeDropperWin& operator=(const EyeDropperWin&) = delete;
~EyeDropperWin() override;
EyeDropperViewAura(const EyeDropperViewAura&) = delete;
EyeDropperViewAura& operator=(const EyeDropperViewAura&) = delete;
~EyeDropperViewAura() override;
protected:
// views::WidgetDelegateView:
......@@ -53,4 +53,4 @@ class EyeDropperWin : public content::EyeDropper,
base::Optional<SkColor> selected_color_;
};
#endif // CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_WIN_H_
#endif // CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_AURA_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_MAC_H_
#define CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_MAC_H_
#include "base/mac/scoped_nsobject.h"
#include "content/public/browser/eye_dropper.h"
#include "content/public/browser/eye_dropper_listener.h"
@class NSColorSampler;
class EyeDropperViewMac : public content::EyeDropper {
public:
EyeDropperViewMac(content::EyeDropperListener* listener);
EyeDropperViewMac(const EyeDropperViewMac&) = delete;
EyeDropperViewMac& operator=(const EyeDropperViewMac&) = delete;
~EyeDropperViewMac() override;
private:
// Receives the color selection.
content::EyeDropperListener* listener_;
base::scoped_nsobject<NSColorSampler> color_sampler_;
};
#endif // CHROME_BROWSER_UI_VIEWS_EYE_DROPPER_EYE_DROPPER_VIEW_MAC_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/eye_dropper/eye_dropper_view_mac.h"
#import <Cocoa/Cocoa.h>
#include "content/public/browser/render_frame_host.h"
#include "skia/ext/skia_utils_mac.h"
EyeDropperViewMac::EyeDropperViewMac(content::EyeDropperListener* listener)
: listener_(listener) {
if (!listener_)
return;
if (@available(macOS 10.15, *)) {
color_sampler_.reset([[NSColorSampler alloc] init]);
[color_sampler_ showSamplerWithSelectionHandler:^(NSColor* selectedColor) {
if (!selectedColor) {
listener_->ColorSelectionCanceled();
} else {
listener_->ColorSelected(skia::NSSystemColorToSkColor(selectedColor));
}
}];
}
}
EyeDropperViewMac::~EyeDropperViewMac() {}
std::unique_ptr<content::EyeDropper> ShowEyeDropper(
content::RenderFrameHost* frame,
content::EyeDropperListener* listener) {
if (@available(macOS 10.15, *)) {
return std::make_unique<EyeDropperViewMac>(listener);
}
return nullptr;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment