electron/shell/browser/ui/tray_icon_cocoa.mm
electron-roller[bot] 8532e1239e
chore: bump chromium to 93.0.4530.0 (master) (#29256)
* chore: bump chromium in DEPS to 92.0.4512.6

* 2887336: [CaptureHandle][#2] Propagate CaptureHandleConfig in browser process

https://chromium-review.googlesource.com/c/chromium/src/+/2887336

* refactor: base::Optional -> absl::optional

* chore: fixup patch indices

* chore: bump chromium in DEPS to 92.0.4514.0

* 2899417: Make build work when enable_pdf is set to false.

https://chromium-review.googlesource.com/c/chromium/src/+/2899417

* 2904731: use BrowserContext instead of Profile in PreconnectManager

https://chromium-review.googlesource.com/c/chromium/src/+/2904731

* 2295749: fix: check IsSecureEventInputEnabled in constructor before setting SetPasswordInputEnabled to true

https://chromium-review.googlesource.com/c/chromium/src/+/2295749

* 2893803: Add a GetWebView to RenderFrame.

https://chromium-review.googlesource.com/c/chromium/src/+/2893803

* 2892345: Implement WebContents::ForEachRenderFrameHost

https://chromium-review.googlesource.com/c/chromium/src/+/2892345

* chore: fixup patch indices

* 2892048: Real instance methods for BrowserContext: remaining 5 methods.

https://chromium-review.googlesource.com/c/chromium/src/+/2892048

* 2902821: [mojo] Don't require full header includes for referenced interfaces

https://chromium-review.googlesource.com/c/chromium/src/+/2902821

* 2496500: Remove last deprecated extension Event ctor.

https://chromium-review.googlesource.com/c/chromium/src/+/2496500

* chore: fixup malformed pepper support patch

* chore: bump chromium in DEPS to 92.0.4515.0

* 2908461: Add CreateEmptyPrintPagesParamsPtr() inside print_view_manager_base.cc.

https://chromium-review.googlesource.com/c/chromium/src/+/2908461

* 2880838: viz: add optional HDRMetadata to TransferableResource

https://chromium-review.googlesource.com/c/chromium/src/+/2880838

* chore: fixup patch indices

* chore: bump chromium in DEPS to 92.0.4515.5

* chore: update patches

* chore: bump chromium in DEPS to 92.0.4515.7

* chore: bump chromium in DEPS to 92.0.4515.9

* chore: bump chromium in DEPS to 93.0.4522.0

* chore: bump chromium in DEPS to 93.0.4523.0

* chore: bump chromium in DEPS to 93.0.4524.0

* chore: update patches

* chore: enable_pak_file_integrity_checks was reverted

* chore: update patches

* refactor: base/optional was replaced with absl::optional

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2910202

* refactor: replace all usages of base::nullopt with absl::nullopt

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2910202

* chore: add missing base::Contains include

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2910202

* refactor: replace all usages of base::make_optional with
absl::make_optional

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2910202

* refactor: replace WorldScriptContext() with GetScriptContextFromWorldId

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2893213

* chore: clean up left over opening namespace

Refs: 95bfe6d08f

* chore: add missing base::Contains include

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2910202

* refactor: replace GetCurrentDisplayIterator with the hard checker
GetCurrentDisplay

This code looks suspicious but if the iterator was invalid before it
will also be invalid now.

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2893191

* refactor: headers are now passed directly in extensions client

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2918906

* refactor: base::DictionaryValue::empty() has been removed

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2912424

* chore: add missing includes for network URLLoaderFactory

Refs: unknown, probably a side effect of header changes

* refactor: make convenience wrapper around AppendArg

There is no converter FromV8 for base::StringPiece (apparently its not
possible).  So we now take in an std::string and use the construct for
StringPiece to do implicit conversion.

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2905544

* chore: add patch

* chore: bump chromium in DEPS to 93.0.4525.0

* chore: update patches

* refactor: CanResize has been de-virtualized

Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2485774

* chore: update resource integrity patch

* chore: add character encoding idl patch

* chore: bump chromium in DEPS to 93.0.4526.0

* chore: update patches

* chore: bump chromium in DEPS to 93.0.4527.0

* chore: bump chromium in DEPS to 93.0.4528.0

* chore: update patches

* chore: update idl encoding patch

* chore: bump chromium in DEPS to 93.0.4529.0

* chore: update patches

* chore: bump chromium in DEPS to 93.0.4530.0

* chore: update patches

* fix: only SetCanResize after the widget has been initialized

* chore: add patch for vr on windows gn gen

* spec: fix focus related tests on linux due to delay in focus swap

* chore: remove new usages of base::Optional from main

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2021-06-03 01:05:04 -07:00

392 lines
12 KiB
Text

// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/ui/tray_icon_cocoa.h"
#include <string>
#include <vector>
#include "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/ui/cocoa/NSString+ANSI.h"
#include "shell/browser/ui/cocoa/electron_menu_controller.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/mac/coordinate_conversion.h"
#include "ui/native_theme/native_theme.h"
@interface StatusItemView : NSView {
electron::TrayIconCocoa* trayIcon_; // weak
ElectronMenuController* menuController_; // weak
BOOL ignoreDoubleClickEvents_;
base::scoped_nsobject<NSStatusItem> statusItem_;
base::scoped_nsobject<NSTrackingArea> trackingArea_;
}
@end // @interface StatusItemView
@implementation StatusItemView
- (void)dealloc {
trayIcon_ = nil;
menuController_ = nil;
[super dealloc];
}
- (id)initWithIcon:(electron::TrayIconCocoa*)icon {
trayIcon_ = icon;
menuController_ = nil;
ignoreDoubleClickEvents_ = NO;
if ((self = [super initWithFrame:CGRectZero])) {
[self registerForDraggedTypes:@[
NSFilenamesPboardType,
NSStringPboardType,
]];
// Create the status item.
NSStatusItem* item = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
statusItem_.reset([item retain]);
[[statusItem_ button] addSubview:self]; // inject custom view
[self updateDimensions];
}
return self;
}
- (void)updateDimensions {
[self setFrame:[statusItem_ button].frame];
}
- (void)updateTrackingAreas {
// Use NSTrackingArea for listening to mouseEnter, mouseExit, and mouseMove
// events.
[self removeTrackingArea:trackingArea_];
trackingArea_.reset([[NSTrackingArea alloc]
initWithRect:[self bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
NSTrackingActiveAlways
owner:self
userInfo:nil]);
[self addTrackingArea:trackingArea_];
}
- (void)removeItem {
// Turn off tracking events to prevent crash.
if (trackingArea_) {
[self removeTrackingArea:trackingArea_];
trackingArea_.reset();
}
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
[self removeFromSuperview];
statusItem_.reset();
}
- (void)setImage:(NSImage*)image {
[[statusItem_ button] setImage:image];
[self updateDimensions];
}
- (void)setAlternateImage:(NSImage*)image {
[[statusItem_ button] setAlternateImage:image];
}
- (void)setIgnoreDoubleClickEvents:(BOOL)ignore {
ignoreDoubleClickEvents_ = ignore;
}
- (BOOL)getIgnoreDoubleClickEvents {
return ignoreDoubleClickEvents_;
}
- (void)setTitle:(NSString*)title font_type:(NSString*)font_type {
NSMutableAttributedString* attributed_title =
[[NSMutableAttributedString alloc] initWithString:title];
if ([title containsANSICodes]) {
attributed_title = [title attributedStringParsingANSICodes];
}
// Change font type, if specified
CGFloat existing_size = [[[statusItem_ button] font] pointSize];
if ([font_type isEqualToString:@"monospaced"]) {
if (@available(macOS 10.15, *)) {
NSDictionary* attributes = @{
NSFontAttributeName :
[NSFont monospacedSystemFontOfSize:existing_size
weight:NSFontWeightRegular]
};
[attributed_title
setAttributes:attributes
range:NSMakeRange(0, [attributed_title length])];
}
} else if ([font_type isEqualToString:@"monospacedDigit"]) {
if (@available(macOS 10.11, *)) {
NSDictionary* attributes = @{
NSFontAttributeName :
[NSFont monospacedDigitSystemFontOfSize:existing_size
weight:NSFontWeightRegular]
};
[attributed_title
setAttributes:attributes
range:NSMakeRange(0, [attributed_title length])];
}
}
// Set title
[[statusItem_ button] setAttributedTitle:attributed_title];
// Fix icon margins.
if (title.length > 0) {
[[statusItem_ button] setImagePosition:NSImageLeft];
} else {
[[statusItem_ button] setImagePosition:NSImageOnly];
}
[self updateDimensions];
}
- (NSString*)title {
return [statusItem_ button].title;
}
- (void)setMenuController:(ElectronMenuController*)menu {
menuController_ = menu;
[statusItem_ setMenu:[menuController_ menu]];
}
- (void)handleClickNotifications:(NSEvent*)event {
// If we are ignoring double click events, we should ignore the `clickCount`
// value and immediately emit a click event.
BOOL shouldBeHandledAsASingleClick =
(event.clickCount == 1) || ignoreDoubleClickEvents_;
if (shouldBeHandledAsASingleClick)
trayIcon_->NotifyClicked(
gfx::ScreenRectFromNSRect(event.window.frame),
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
// Double click event.
BOOL shouldBeHandledAsADoubleClick =
(event.clickCount == 2) && !ignoreDoubleClickEvents_;
if (shouldBeHandledAsADoubleClick)
trayIcon_->NotifyDoubleClicked(
gfx::ScreenRectFromNSRect(event.window.frame),
ui::EventFlagsFromModifiers([event modifierFlags]));
}
- (void)mouseDown:(NSEvent*)event {
trayIcon_->NotifyMouseDown(
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
// Pass click to superclass to show menu. Custom mouseUp handler won't be
// invoked.
if (menuController_) {
[self handleClickNotifications:event];
[super mouseDown:event];
} else {
[[statusItem_ button] highlight:YES];
}
}
- (void)mouseUp:(NSEvent*)event {
[[statusItem_ button] highlight:NO];
trayIcon_->NotifyMouseUp(
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
[self handleClickNotifications:event];
}
- (void)popUpContextMenu:(electron::ElectronMenuModel*)menu_model {
// Make sure events can be pumped while the menu is up.
base::CurrentThread::ScopedNestableTaskAllower allow;
// Show a custom menu.
if (menu_model) {
base::scoped_nsobject<ElectronMenuController> menuController(
[[ElectronMenuController alloc] initWithModel:menu_model
useDefaultAccelerator:NO]);
// Hacky way to mimic design of ordinary tray menu.
[statusItem_ setMenu:[menuController menu]];
// -performClick: is a blocking call, which will run the task loop inside
// itself. This can potentially include running JS, which can result in
// this object being released. We take a temporary reference here to make
// sure we stay alive long enough to successfully return from this
// function.
// TODO(nornagon/codebytere): Avoid nesting task loops here.
[self retain];
[[statusItem_ button] performClick:self];
[statusItem_ setMenu:[menuController_ menu]];
[self release];
return;
}
if (menuController_ && ![menuController_ isMenuOpen]) {
// Ensure the UI can update while the menu is fading out.
base::ScopedPumpMessagesInPrivateModes pump_private;
[[statusItem_ button] performClick:self];
}
}
- (void)closeContextMenu {
if (menuController_) {
[menuController_ cancel];
}
}
- (void)rightMouseUp:(NSEvent*)event {
trayIcon_->NotifyRightClicked(
gfx::ScreenRectFromNSRect(event.window.frame),
ui::EventFlagsFromModifiers([event modifierFlags]));
}
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
trayIcon_->NotifyDragEntered();
return NSDragOperationCopy;
}
- (void)mouseExited:(NSEvent*)event {
trayIcon_->NotifyMouseExited(
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
}
- (void)mouseEntered:(NSEvent*)event {
trayIcon_->NotifyMouseEntered(
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
}
- (void)mouseMoved:(NSEvent*)event {
trayIcon_->NotifyMouseMoved(
gfx::ScreenPointFromNSPoint([event locationInWindow]),
ui::EventFlagsFromModifiers([event modifierFlags]));
}
- (void)draggingExited:(id<NSDraggingInfo>)sender {
trayIcon_->NotifyDragExited();
}
- (void)draggingEnded:(id<NSDraggingInfo>)sender {
trayIcon_->NotifyDragEnded();
if (NSPointInRect([sender draggingLocation], self.frame)) {
trayIcon_->NotifyDrop();
}
}
- (BOOL)handleDrop:(id<NSDraggingInfo>)sender {
NSPasteboard* pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
std::vector<std::string> dropFiles;
NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
for (NSString* file in files)
dropFiles.push_back(base::SysNSStringToUTF8(file));
trayIcon_->NotifyDropFiles(dropFiles);
return YES;
} else if ([[pboard types] containsObject:NSStringPboardType]) {
NSString* dropText = [pboard stringForType:NSStringPboardType];
trayIcon_->NotifyDropText(base::SysNSStringToUTF8(dropText));
return YES;
}
return NO;
}
- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender {
return YES;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
[self handleDrop:sender];
return YES;
}
@end
namespace electron {
TrayIconCocoa::TrayIconCocoa() {
status_item_view_.reset([[StatusItemView alloc] initWithIcon:this]);
}
TrayIconCocoa::~TrayIconCocoa() {
[status_item_view_ removeItem];
}
void TrayIconCocoa::SetImage(const gfx::Image& image) {
[status_item_view_ setImage:image.AsNSImage()];
}
void TrayIconCocoa::SetPressedImage(const gfx::Image& image) {
[status_item_view_ setAlternateImage:image.AsNSImage()];
}
void TrayIconCocoa::SetToolTip(const std::string& tool_tip) {
[status_item_view_ setToolTip:base::SysUTF8ToNSString(tool_tip)];
}
void TrayIconCocoa::SetTitle(const std::string& title,
const TitleOptions& options) {
[status_item_view_ setTitle:base::SysUTF8ToNSString(title)
font_type:base::SysUTF8ToNSString(options.font_type)];
}
std::string TrayIconCocoa::GetTitle() {
return base::SysNSStringToUTF8([status_item_view_ title]);
}
void TrayIconCocoa::SetIgnoreDoubleClickEvents(bool ignore) {
[status_item_view_ setIgnoreDoubleClickEvents:ignore];
}
bool TrayIconCocoa::GetIgnoreDoubleClickEvents() {
return [status_item_view_ getIgnoreDoubleClickEvents];
}
void TrayIconCocoa::PopUpOnUI(ElectronMenuModel* menu_model) {
[status_item_view_ popUpContextMenu:menu_model];
}
void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos,
ElectronMenuModel* menu_model) {
base::PostTask(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&TrayIconCocoa::PopUpOnUI, weak_factory_.GetWeakPtr(),
base::Unretained(menu_model)));
}
void TrayIconCocoa::CloseContextMenu() {
[status_item_view_ closeContextMenu];
}
void TrayIconCocoa::SetContextMenu(ElectronMenuModel* menu_model) {
if (menu_model) {
// Create native menu.
menu_.reset([[ElectronMenuController alloc] initWithModel:menu_model
useDefaultAccelerator:NO]);
} else {
menu_.reset();
}
[status_item_view_ setMenuController:menu_.get()];
}
gfx::Rect TrayIconCocoa::GetBounds() {
return gfx::ScreenRectFromNSRect([status_item_view_ window].frame);
}
// static
TrayIcon* TrayIcon::Create(absl::optional<UUID> guid) {
return new TrayIconCocoa;
}
} // namespace electron