native_window_mac.mm 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/native_window_mac.h"
  5. #include <AvailabilityMacros.h>
  6. #include <objc/objc-runtime.h>
  7. #include <algorithm>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/cxx17_backports.h"
  12. #include "base/mac/mac_util.h"
  13. #include "base/mac/scoped_cftyperef.h"
  14. #include "base/strings/sys_string_conversions.h"
  15. #include "base/task/post_task.h"
  16. #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
  17. #include "components/remote_cocoa/browser/scoped_cg_window_id.h"
  18. #include "content/public/browser/browser_accessibility_state.h"
  19. #include "content/public/browser/browser_task_traits.h"
  20. #include "content/public/browser/browser_thread.h"
  21. #include "content/public/browser/desktop_media_id.h"
  22. #include "shell/browser/javascript_environment.h"
  23. #include "shell/browser/native_browser_view_mac.h"
  24. #include "shell/browser/ui/cocoa/electron_native_widget_mac.h"
  25. #include "shell/browser/ui/cocoa/electron_ns_window.h"
  26. #include "shell/browser/ui/cocoa/electron_ns_window_delegate.h"
  27. #include "shell/browser/ui/cocoa/electron_preview_item.h"
  28. #include "shell/browser/ui/cocoa/electron_touch_bar.h"
  29. #include "shell/browser/ui/cocoa/root_view_mac.h"
  30. #include "shell/browser/ui/cocoa/window_buttons_proxy.h"
  31. #include "shell/browser/ui/inspectable_web_contents.h"
  32. #include "shell/browser/ui/inspectable_web_contents_view.h"
  33. #include "shell/browser/window_list.h"
  34. #include "shell/common/gin_converters/gfx_converter.h"
  35. #include "shell/common/gin_helper/dictionary.h"
  36. #include "shell/common/node_includes.h"
  37. #include "shell/common/options_switches.h"
  38. #include "shell/common/process_util.h"
  39. #include "skia/ext/skia_utils_mac.h"
  40. #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h"
  41. #include "ui/display/screen.h"
  42. #include "ui/gfx/skia_util.h"
  43. #include "ui/gl/gpu_switching_manager.h"
  44. #include "ui/views/background.h"
  45. #include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
  46. #include "ui/views/widget/widget.h"
  47. // This view would inform Chromium to resize the hosted views::View.
  48. //
  49. // The overrided methods should behave the same with BridgedContentView.
  50. @interface ElectronAdaptedContentView : NSView {
  51. @private
  52. views::NativeWidgetMacNSWindowHost* bridge_host_;
  53. }
  54. @end
  55. @implementation ElectronAdaptedContentView
  56. - (id)initWithShell:(electron::NativeWindowMac*)shell {
  57. if ((self = [self init])) {
  58. bridge_host_ = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(
  59. shell->GetNativeWindow());
  60. }
  61. return self;
  62. }
  63. - (void)viewDidMoveToWindow {
  64. // When this view is added to a window, AppKit calls setFrameSize before it is
  65. // added to the window, so the behavior in setFrameSize is not triggered.
  66. NSWindow* window = [self window];
  67. if (window)
  68. [self setFrameSize:NSZeroSize];
  69. }
  70. - (void)setFrameSize:(NSSize)newSize {
  71. // The size passed in here does not always use
  72. // -[NSWindow contentRectForFrameRect]. The following ensures that the
  73. // contentView for a frameless window can extend over the titlebar of the new
  74. // window containing it, since AppKit requires a titlebar to give frameless
  75. // windows correct shadows and rounded corners.
  76. NSWindow* window = [self window];
  77. if (window && [window contentView] == self) {
  78. newSize = [window contentRectForFrameRect:[window frame]].size;
  79. // Ensure that the window geometry be updated on the host side before the
  80. // view size is updated.
  81. bridge_host_->GetInProcessNSWindowBridge()->UpdateWindowGeometry();
  82. }
  83. [super setFrameSize:newSize];
  84. // The OnViewSizeChanged is marked private in derived class.
  85. static_cast<remote_cocoa::mojom::NativeWidgetNSWindowHost*>(bridge_host_)
  86. ->OnViewSizeChanged(gfx::Size(newSize.width, newSize.height));
  87. }
  88. @end
  89. // This view always takes the size of its superview. It is intended to be used
  90. // as a NSWindow's contentView. It is needed because NSWindow's implementation
  91. // explicitly resizes the contentView at inopportune times.
  92. @interface FullSizeContentView : NSView
  93. @end
  94. @implementation FullSizeContentView
  95. // This method is directly called by NSWindow during a window resize on OSX
  96. // 10.10.0, beta 2. We must override it to prevent the content view from
  97. // shrinking.
  98. - (void)setFrameSize:(NSSize)size {
  99. if ([self superview])
  100. size = [[self superview] bounds].size;
  101. [super setFrameSize:size];
  102. }
  103. // The contentView gets moved around during certain full-screen operations.
  104. // This is less than ideal, and should eventually be removed.
  105. - (void)viewDidMoveToSuperview {
  106. [self setFrame:[[self superview] bounds]];
  107. }
  108. @end
  109. @interface ElectronProgressBar : NSProgressIndicator
  110. @end
  111. @implementation ElectronProgressBar
  112. - (void)drawRect:(NSRect)dirtyRect {
  113. if (self.style != NSProgressIndicatorBarStyle)
  114. return;
  115. // Draw edges of rounded rect.
  116. NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
  117. CGFloat radius = rect.size.height / 2;
  118. NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
  119. xRadius:radius
  120. yRadius:radius];
  121. [bezier_path setLineWidth:2.0];
  122. [[NSColor grayColor] set];
  123. [bezier_path stroke];
  124. // Fill the rounded rect.
  125. rect = NSInsetRect(rect, 2.0, 2.0);
  126. radius = rect.size.height / 2;
  127. bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
  128. xRadius:radius
  129. yRadius:radius];
  130. [bezier_path setLineWidth:1.0];
  131. [bezier_path addClip];
  132. // Calculate the progress width.
  133. rect.size.width =
  134. floor(rect.size.width * ([self doubleValue] / [self maxValue]));
  135. // Fill the progress bar with color blue.
  136. [[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1 alpha:1] set];
  137. NSRectFill(rect);
  138. }
  139. @end
  140. namespace gin {
  141. template <>
  142. struct Converter<electron::NativeWindowMac::VisualEffectState> {
  143. static bool FromV8(v8::Isolate* isolate,
  144. v8::Handle<v8::Value> val,
  145. electron::NativeWindowMac::VisualEffectState* out) {
  146. using VisualEffectState = electron::NativeWindowMac::VisualEffectState;
  147. std::string visual_effect_state;
  148. if (!ConvertFromV8(isolate, val, &visual_effect_state))
  149. return false;
  150. if (visual_effect_state == "followWindow") {
  151. *out = VisualEffectState::kFollowWindow;
  152. } else if (visual_effect_state == "active") {
  153. *out = VisualEffectState::kActive;
  154. } else if (visual_effect_state == "inactive") {
  155. *out = VisualEffectState::kInactive;
  156. } else {
  157. return false;
  158. }
  159. return true;
  160. }
  161. };
  162. } // namespace gin
  163. namespace electron {
  164. namespace {
  165. bool IsFramelessWindow(NSView* view) {
  166. NSWindow* nswindow = [view window];
  167. if (![nswindow respondsToSelector:@selector(shell)])
  168. return false;
  169. NativeWindow* window = [static_cast<ElectronNSWindow*>(nswindow) shell];
  170. return window && !window->has_frame();
  171. }
  172. IMP original_set_frame_size = nullptr;
  173. IMP original_view_did_move_to_superview = nullptr;
  174. // This method is directly called by NSWindow during a window resize on OSX
  175. // 10.10.0, beta 2. We must override it to prevent the content view from
  176. // shrinking.
  177. void SetFrameSize(NSView* self, SEL _cmd, NSSize size) {
  178. if (!IsFramelessWindow(self)) {
  179. auto original =
  180. reinterpret_cast<decltype(&SetFrameSize)>(original_set_frame_size);
  181. return original(self, _cmd, size);
  182. }
  183. // For frameless window, resize the view to cover full window.
  184. if ([self superview])
  185. size = [[self superview] bounds].size;
  186. auto super_impl = reinterpret_cast<decltype(&SetFrameSize)>(
  187. [[self superclass] instanceMethodForSelector:_cmd]);
  188. super_impl(self, _cmd, size);
  189. }
  190. // The contentView gets moved around during certain full-screen operations.
  191. // This is less than ideal, and should eventually be removed.
  192. void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
  193. if (!IsFramelessWindow(self)) {
  194. // [BridgedContentView viewDidMoveToSuperview];
  195. auto original = reinterpret_cast<decltype(&ViewDidMoveToSuperview)>(
  196. original_view_did_move_to_superview);
  197. if (original)
  198. original(self, _cmd);
  199. return;
  200. }
  201. [self setFrame:[[self superview] bounds]];
  202. }
  203. } // namespace
  204. NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
  205. NativeWindow* parent)
  206. : NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
  207. ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
  208. display::Screen::GetScreen()->AddObserver(this);
  209. int width = 800, height = 600;
  210. options.Get(options::kWidth, &width);
  211. options.Get(options::kHeight, &height);
  212. NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame];
  213. gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2),
  214. round((NSHeight(main_screen_rect) - height) / 2), width,
  215. height);
  216. bool resizable = true;
  217. options.Get(options::kResizable, &resizable);
  218. options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
  219. options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
  220. options.GetOptional(options::kTrafficLightPosition, &traffic_light_position_);
  221. options.Get(options::kVisualEffectState, &visual_effect_state_);
  222. if (options.Has(options::kFullscreenWindowTitle)) {
  223. EmitWarning(
  224. node::Environment::GetCurrent(JavascriptEnvironment::GetIsolate()),
  225. "\"fullscreenWindowTitle\" option has been deprecated and is "
  226. "no-op now.",
  227. "electron");
  228. }
  229. bool minimizable = true;
  230. options.Get(options::kMinimizable, &minimizable);
  231. bool maximizable = true;
  232. options.Get(options::kMaximizable, &maximizable);
  233. bool closable = true;
  234. options.Get(options::kClosable, &closable);
  235. std::string tabbingIdentifier;
  236. options.Get(options::kTabbingIdentifier, &tabbingIdentifier);
  237. std::string windowType;
  238. options.Get(options::kType, &windowType);
  239. bool useStandardWindow = true;
  240. // eventually deprecate separate "standardWindow" option in favor of
  241. // standard / textured window types
  242. options.Get(options::kStandardWindow, &useStandardWindow);
  243. if (windowType == "textured") {
  244. useStandardWindow = false;
  245. }
  246. // The window without titlebar is treated the same with frameless window.
  247. if (title_bar_style_ != TitleBarStyle::kNormal)
  248. set_has_frame(false);
  249. NSUInteger styleMask = NSWindowStyleMaskTitled;
  250. // Removing NSWindowStyleMaskTitled removes window title, which removes
  251. // rounded corners of window.
  252. bool rounded_corner = true;
  253. options.Get(options::kRoundedCorners, &rounded_corner);
  254. if (!rounded_corner && !has_frame())
  255. styleMask = 0;
  256. if (minimizable)
  257. styleMask |= NSMiniaturizableWindowMask;
  258. if (closable)
  259. styleMask |= NSWindowStyleMaskClosable;
  260. if (resizable)
  261. styleMask |= NSResizableWindowMask;
  262. if (!useStandardWindow || transparent() || !has_frame())
  263. styleMask |= NSTexturedBackgroundWindowMask;
  264. // Create views::Widget and assign window_ with it.
  265. // TODO(zcbenz): Get rid of the window_ in future.
  266. views::Widget::InitParams params;
  267. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  268. params.bounds = bounds;
  269. params.delegate = this;
  270. params.type = views::Widget::InitParams::TYPE_WINDOW;
  271. params.native_widget = new ElectronNativeWidgetMac(this, styleMask, widget());
  272. widget()->Init(std::move(params));
  273. SetCanResize(resizable);
  274. window_ = static_cast<ElectronNSWindow*>(
  275. widget()->GetNativeWindow().GetNativeNSWindow());
  276. RegisterDeleteDelegateCallback(base::BindOnce(
  277. [](NativeWindowMac* window) {
  278. if (window->window_)
  279. window->window_ = nil;
  280. if (window->buttons_proxy_)
  281. window->buttons_proxy_.reset();
  282. },
  283. this));
  284. [window_ setEnableLargerThanScreen:enable_larger_than_screen()];
  285. window_delegate_.reset([[ElectronNSWindowDelegate alloc] initWithShell:this]);
  286. [window_ setDelegate:window_delegate_];
  287. // Only use native parent window for non-modal windows.
  288. if (parent && !is_modal()) {
  289. SetParentWindow(parent);
  290. }
  291. if (transparent()) {
  292. // Setting the background color to clear will also hide the shadow.
  293. [window_ setBackgroundColor:[NSColor clearColor]];
  294. }
  295. if (windowType == "desktop") {
  296. [window_ setLevel:kCGDesktopWindowLevel - 1];
  297. [window_ setDisableKeyOrMainWindow:YES];
  298. [window_ setCollectionBehavior:(NSWindowCollectionBehaviorCanJoinAllSpaces |
  299. NSWindowCollectionBehaviorStationary |
  300. NSWindowCollectionBehaviorIgnoresCycle)];
  301. }
  302. bool focusable;
  303. if (options.Get(options::kFocusable, &focusable) && !focusable)
  304. [window_ setDisableKeyOrMainWindow:YES];
  305. if (transparent() || !has_frame()) {
  306. // Don't show title bar.
  307. [window_ setTitlebarAppearsTransparent:YES];
  308. [window_ setTitleVisibility:NSWindowTitleHidden];
  309. // Remove non-transparent corners, see http://git.io/vfonD.
  310. [window_ setOpaque:NO];
  311. // Show window buttons if titleBarStyle is not "normal".
  312. if (title_bar_style_ == TitleBarStyle::kNormal) {
  313. InternalSetWindowButtonVisibility(false);
  314. } else {
  315. buttons_proxy_.reset([[WindowButtonsProxy alloc] initWithWindow:window_]);
  316. [buttons_proxy_ setHeight:titlebar_overlay_height()];
  317. if (traffic_light_position_) {
  318. [buttons_proxy_ setMargin:*traffic_light_position_];
  319. } else if (title_bar_style_ == TitleBarStyle::kHiddenInset) {
  320. // For macOS >= 11, while this value does not match offical macOS apps
  321. // like Safari or Notes, it matches titleBarStyle's old implementation
  322. // before Electron <= 12.
  323. [buttons_proxy_ setMargin:gfx::Point(12, 11)];
  324. }
  325. if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover) {
  326. [buttons_proxy_ setShowOnHover:YES];
  327. } else {
  328. // customButtonsOnHover does not show buttons initialiy.
  329. InternalSetWindowButtonVisibility(true);
  330. }
  331. }
  332. }
  333. // Create a tab only if tabbing identifier is specified and window has
  334. // a native title bar.
  335. if (tabbingIdentifier.empty() || transparent() || !has_frame()) {
  336. if (@available(macOS 10.12, *)) {
  337. [window_ setTabbingMode:NSWindowTabbingModeDisallowed];
  338. }
  339. } else {
  340. if (@available(macOS 10.12, *)) {
  341. [window_ setTabbingIdentifier:base::SysUTF8ToNSString(tabbingIdentifier)];
  342. }
  343. }
  344. // Resize to content bounds.
  345. bool use_content_size = false;
  346. options.Get(options::kUseContentSize, &use_content_size);
  347. if (!has_frame() || use_content_size)
  348. SetContentSize(gfx::Size(width, height));
  349. // Enable the NSView to accept first mouse event.
  350. bool acceptsFirstMouse = false;
  351. options.Get(options::kAcceptFirstMouse, &acceptsFirstMouse);
  352. [window_ setAcceptsFirstMouse:acceptsFirstMouse];
  353. // Disable auto-hiding cursor.
  354. bool disableAutoHideCursor = false;
  355. options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
  356. [window_ setDisableAutoHideCursor:disableAutoHideCursor];
  357. // Use an NSEvent monitor to listen for the wheel event.
  358. BOOL __block began = NO;
  359. wheel_event_monitor_ = [NSEvent
  360. addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
  361. handler:^(NSEvent* event) {
  362. if ([[event window] windowNumber] !=
  363. [window_ windowNumber])
  364. return event;
  365. if (!began && (([event phase] ==
  366. NSEventPhaseMayBegin) ||
  367. ([event phase] ==
  368. NSEventPhaseBegan))) {
  369. this->NotifyWindowScrollTouchBegin();
  370. began = YES;
  371. } else if (began &&
  372. (([event phase] ==
  373. NSEventPhaseEnded) ||
  374. ([event phase] ==
  375. NSEventPhaseCancelled))) {
  376. this->NotifyWindowScrollTouchEnd();
  377. began = NO;
  378. }
  379. return event;
  380. }];
  381. // Set maximizable state last to ensure zoom button does not get reset
  382. // by calls to other APIs.
  383. SetMaximizable(maximizable);
  384. // Default content view.
  385. SetContentView(new views::View());
  386. AddContentViewLayers();
  387. original_frame_ = [window_ frame];
  388. original_level_ = [window_ level];
  389. }
  390. NativeWindowMac::~NativeWindowMac() = default;
  391. void NativeWindowMac::SetContentView(views::View* view) {
  392. views::View* root_view = GetContentsView();
  393. if (content_view())
  394. root_view->RemoveChildView(content_view());
  395. set_content_view(view);
  396. root_view->AddChildView(content_view());
  397. root_view->Layout();
  398. }
  399. void NativeWindowMac::Close() {
  400. if (!IsClosable()) {
  401. WindowList::WindowCloseCancelled(this);
  402. return;
  403. }
  404. // If a sheet is attached to the window when we call
  405. // [window_ performClose:nil], the window won't close properly
  406. // even after the user has ended the sheet.
  407. // Ensure it's closed before calling [window_ performClose:nil].
  408. SetEnabled(true);
  409. [window_ performClose:nil];
  410. // Closing a sheet doesn't trigger windowShouldClose,
  411. // so we need to manually call it ourselves here.
  412. if (is_modal() && parent() && IsVisible()) {
  413. NotifyWindowCloseButtonClicked();
  414. }
  415. }
  416. void NativeWindowMac::CloseImmediately() {
  417. // Retain the child window before closing it. If the last reference to the
  418. // NSWindow goes away inside -[NSWindow close], then bad stuff can happen.
  419. // See e.g. http://crbug.com/616701.
  420. base::scoped_nsobject<NSWindow> child_window(window_,
  421. base::scoped_policy::RETAIN);
  422. [window_ close];
  423. }
  424. void NativeWindowMac::Focus(bool focus) {
  425. if (!IsVisible())
  426. return;
  427. if (focus) {
  428. [[NSApplication sharedApplication] activateIgnoringOtherApps:NO];
  429. [window_ makeKeyAndOrderFront:nil];
  430. } else {
  431. [window_ orderBack:nil];
  432. }
  433. }
  434. bool NativeWindowMac::IsFocused() {
  435. return [window_ isKeyWindow];
  436. }
  437. void NativeWindowMac::Show() {
  438. if (is_modal() && parent()) {
  439. NSWindow* window = parent()->GetNativeWindow().GetNativeNSWindow();
  440. if ([window_ sheetParent] == nil)
  441. [window beginSheet:window_
  442. completionHandler:^(NSModalResponse){
  443. }];
  444. return;
  445. }
  446. // Reattach the window to the parent to actually show it.
  447. if (parent())
  448. InternalSetParentWindow(parent(), true);
  449. // This method is supposed to put focus on window, however if the app does not
  450. // have focus then "makeKeyAndOrderFront" will only show the window.
  451. [NSApp activateIgnoringOtherApps:YES];
  452. [window_ makeKeyAndOrderFront:nil];
  453. }
  454. void NativeWindowMac::ShowInactive() {
  455. // Reattach the window to the parent to actually show it.
  456. if (parent())
  457. InternalSetParentWindow(parent(), true);
  458. [window_ orderFrontRegardless];
  459. }
  460. void NativeWindowMac::Hide() {
  461. // If a sheet is attached to the window when we call [window_ orderOut:nil],
  462. // the sheet won't be able to show again on the same window.
  463. // Ensure it's closed before calling [window_ orderOut:nil].
  464. SetEnabled(true);
  465. if (is_modal() && parent()) {
  466. [window_ orderOut:nil];
  467. [parent()->GetNativeWindow().GetNativeNSWindow() endSheet:window_];
  468. return;
  469. }
  470. // Hide all children of the current window before hiding the window.
  471. // components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
  472. // expects this when window visibility changes.
  473. if ([window_ childWindows]) {
  474. for (NSWindow* child in [window_ childWindows]) {
  475. [child orderOut:nil];
  476. }
  477. }
  478. // Deattach the window from the parent before.
  479. if (parent())
  480. InternalSetParentWindow(parent(), false);
  481. [window_ orderOut:nil];
  482. }
  483. bool NativeWindowMac::IsVisible() {
  484. bool occluded = [window_ occlusionState] == NSWindowOcclusionStateVisible;
  485. // For a window to be visible, it must be visible to the user in the
  486. // foreground of the app, which means that it should not be minimized or
  487. // occluded
  488. return [window_ isVisible] && !occluded && !IsMinimized();
  489. }
  490. void NativeWindowMac::SetFullScreenTransitionState(
  491. FullScreenTransitionState state) {
  492. fullscreen_transition_state_ = state;
  493. }
  494. bool NativeWindowMac::IsEnabled() {
  495. return [window_ attachedSheet] == nil;
  496. }
  497. void NativeWindowMac::SetEnabled(bool enable) {
  498. if (!enable) {
  499. [window_ beginSheet:window_
  500. completionHandler:^(NSModalResponse returnCode) {
  501. NSLog(@"modal enabled");
  502. return;
  503. }];
  504. } else if ([window_ attachedSheet]) {
  505. [window_ endSheet:[window_ attachedSheet]];
  506. }
  507. }
  508. void NativeWindowMac::Maximize() {
  509. const bool is_visible = [window_ isVisible];
  510. if (IsMaximized()) {
  511. if (!is_visible)
  512. ShowInactive();
  513. return;
  514. }
  515. // Take note of the current window size
  516. if (IsNormal())
  517. original_frame_ = [window_ frame];
  518. [window_ zoom:nil];
  519. if (!is_visible) {
  520. ShowInactive();
  521. NotifyWindowMaximize();
  522. }
  523. }
  524. void NativeWindowMac::Unmaximize() {
  525. // Bail if the last user set bounds were the same size as the window
  526. // screen (e.g. the user set the window to maximized via setBounds)
  527. //
  528. // Per docs during zoom:
  529. // > If there’s no saved user state because there has been no previous
  530. // > zoom,the size and location of the window don’t change.
  531. //
  532. // However, in classic Apple fashion, this is not the case in practice,
  533. // and the frame inexplicably becomes very tiny. We should prevent
  534. // zoom from being called if the window is being unmaximized and its
  535. // unmaximized window bounds are themselves functionally maximized.
  536. if (!IsMaximized() || user_set_bounds_maximized_)
  537. return;
  538. [window_ zoom:nil];
  539. }
  540. bool NativeWindowMac::IsMaximized() {
  541. if (([window_ styleMask] & NSWindowStyleMaskResizable) != 0)
  542. return [window_ isZoomed];
  543. NSRect rectScreen = GetAspectRatio() > 0.0
  544. ? default_frame_for_zoom()
  545. : [[NSScreen mainScreen] visibleFrame];
  546. return NSEqualRects([window_ frame], rectScreen);
  547. }
  548. void NativeWindowMac::Minimize() {
  549. if (IsMinimized())
  550. return;
  551. // Take note of the current window size
  552. if (IsNormal())
  553. original_frame_ = [window_ frame];
  554. [window_ miniaturize:nil];
  555. }
  556. void NativeWindowMac::Restore() {
  557. [window_ deminiaturize:nil];
  558. }
  559. bool NativeWindowMac::IsMinimized() {
  560. return [window_ isMiniaturized];
  561. }
  562. void NativeWindowMac::HandlePendingFullscreenTransitions() {
  563. if (pending_transitions_.empty())
  564. return;
  565. bool next_transition = pending_transitions_.front();
  566. pending_transitions_.pop();
  567. SetFullScreen(next_transition);
  568. }
  569. void NativeWindowMac::SetFullScreen(bool fullscreen) {
  570. // [NSWindow -toggleFullScreen] is an asynchronous operation, which means
  571. // that it's possible to call it while a fullscreen transition is currently
  572. // in process. This can create weird behavior (incl. phantom windows),
  573. // so we want to schedule a transition for when the current one has completed.
  574. if (fullscreen_transition_state() != FullScreenTransitionState::NONE) {
  575. if (!pending_transitions_.empty()) {
  576. bool last_pending = pending_transitions_.back();
  577. // Only push new transitions if they're different than the last transition
  578. // in the queue.
  579. if (last_pending != fullscreen)
  580. pending_transitions_.push(fullscreen);
  581. } else {
  582. pending_transitions_.push(fullscreen);
  583. }
  584. return;
  585. }
  586. if (fullscreen == IsFullscreen())
  587. return;
  588. // Take note of the current window size
  589. if (IsNormal())
  590. original_frame_ = [window_ frame];
  591. // This needs to be set here because it can be the case that
  592. // SetFullScreen is called by a user before windowWillEnterFullScreen
  593. // or windowWillExitFullScreen are invoked, and so a potential transition
  594. // could be dropped.
  595. fullscreen_transition_state_ = fullscreen
  596. ? FullScreenTransitionState::ENTERING
  597. : FullScreenTransitionState::EXITING;
  598. [window_ toggleFullScreenMode:nil];
  599. }
  600. bool NativeWindowMac::IsFullscreen() const {
  601. return [window_ styleMask] & NSWindowStyleMaskFullScreen;
  602. }
  603. void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
  604. // Do nothing if in fullscreen mode.
  605. if (IsFullscreen())
  606. return;
  607. // Check size constraints since setFrame does not check it.
  608. gfx::Size size = bounds.size();
  609. size.SetToMax(GetMinimumSize());
  610. gfx::Size max_size = GetMaximumSize();
  611. if (!max_size.IsEmpty())
  612. size.SetToMin(max_size);
  613. NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0, size.width(), size.height());
  614. // Flip coordinates based on the primary screen.
  615. NSScreen* screen = [[NSScreen screens] firstObject];
  616. cocoa_bounds.origin.y = NSHeight([screen frame]) - size.height() - bounds.y();
  617. [window_ setFrame:cocoa_bounds display:YES animate:animate];
  618. user_set_bounds_maximized_ = IsMaximized() ? true : false;
  619. }
  620. gfx::Rect NativeWindowMac::GetBounds() {
  621. NSRect frame = [window_ frame];
  622. gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
  623. NSScreen* screen = [[NSScreen screens] firstObject];
  624. bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
  625. return bounds;
  626. }
  627. bool NativeWindowMac::IsNormal() {
  628. return NativeWindow::IsNormal() && !IsSimpleFullScreen();
  629. }
  630. gfx::Rect NativeWindowMac::GetNormalBounds() {
  631. if (IsNormal()) {
  632. return GetBounds();
  633. }
  634. NSRect frame = original_frame_;
  635. gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
  636. NSScreen* screen = [[NSScreen screens] firstObject];
  637. bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
  638. return bounds;
  639. // Works on OS_WIN !
  640. // return widget()->GetRestoredBounds();
  641. }
  642. void NativeWindowMac::SetContentSizeConstraints(
  643. const extensions::SizeConstraints& size_constraints) {
  644. auto convertSize = [this](const gfx::Size& size) {
  645. // Our frameless window still has titlebar attached, so setting contentSize
  646. // will result in actual content size being larger.
  647. if (!has_frame()) {
  648. NSRect frame = NSMakeRect(0, 0, size.width(), size.height());
  649. NSRect content = [window_ originalContentRectForFrameRect:frame];
  650. return content.size;
  651. } else {
  652. return NSMakeSize(size.width(), size.height());
  653. }
  654. };
  655. NSView* content = [window_ contentView];
  656. if (size_constraints.HasMinimumSize()) {
  657. NSSize min_size = convertSize(size_constraints.GetMinimumSize());
  658. [window_ setContentMinSize:[content convertSize:min_size toView:nil]];
  659. }
  660. if (size_constraints.HasMaximumSize()) {
  661. NSSize max_size = convertSize(size_constraints.GetMaximumSize());
  662. [window_ setContentMaxSize:[content convertSize:max_size toView:nil]];
  663. }
  664. NativeWindow::SetContentSizeConstraints(size_constraints);
  665. }
  666. bool NativeWindowMac::MoveAbove(const std::string& sourceId) {
  667. const content::DesktopMediaID id = content::DesktopMediaID::Parse(sourceId);
  668. if (id.type != content::DesktopMediaID::TYPE_WINDOW)
  669. return false;
  670. // Check if the window source is valid.
  671. const CGWindowID window_id = id.id;
  672. if (!webrtc::GetWindowOwnerPid(window_id))
  673. return false;
  674. [window_ orderWindow:NSWindowAbove relativeTo:id.id];
  675. return true;
  676. }
  677. void NativeWindowMac::MoveTop() {
  678. [window_ orderWindow:NSWindowAbove relativeTo:0];
  679. }
  680. void NativeWindowMac::SetResizable(bool resizable) {
  681. SetStyleMask(resizable, NSWindowStyleMaskResizable);
  682. SetCanResize(resizable);
  683. }
  684. bool NativeWindowMac::IsResizable() {
  685. return [window_ styleMask] & NSWindowStyleMaskResizable;
  686. }
  687. void NativeWindowMac::SetMovable(bool movable) {
  688. [window_ setMovable:movable];
  689. }
  690. bool NativeWindowMac::IsMovable() {
  691. return [window_ isMovable];
  692. }
  693. void NativeWindowMac::SetMinimizable(bool minimizable) {
  694. SetStyleMask(minimizable, NSMiniaturizableWindowMask);
  695. }
  696. bool NativeWindowMac::IsMinimizable() {
  697. return [window_ styleMask] & NSMiniaturizableWindowMask;
  698. }
  699. void NativeWindowMac::SetMaximizable(bool maximizable) {
  700. maximizable_ = maximizable;
  701. [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:maximizable];
  702. }
  703. bool NativeWindowMac::IsMaximizable() {
  704. return [[window_ standardWindowButton:NSWindowZoomButton] isEnabled];
  705. }
  706. void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
  707. SetCollectionBehavior(fullscreenable,
  708. NSWindowCollectionBehaviorFullScreenPrimary);
  709. // On EL Capitan this flag is required to hide fullscreen button.
  710. SetCollectionBehavior(!fullscreenable,
  711. NSWindowCollectionBehaviorFullScreenAuxiliary);
  712. }
  713. bool NativeWindowMac::IsFullScreenable() {
  714. NSUInteger collectionBehavior = [window_ collectionBehavior];
  715. return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
  716. }
  717. void NativeWindowMac::SetClosable(bool closable) {
  718. SetStyleMask(closable, NSWindowStyleMaskClosable);
  719. }
  720. bool NativeWindowMac::IsClosable() {
  721. return [window_ styleMask] & NSWindowStyleMaskClosable;
  722. }
  723. void NativeWindowMac::SetAlwaysOnTop(ui::ZOrderLevel z_order,
  724. const std::string& level_name,
  725. int relative_level) {
  726. if (z_order == ui::ZOrderLevel::kNormal) {
  727. SetWindowLevel(NSNormalWindowLevel);
  728. return;
  729. }
  730. int level = NSNormalWindowLevel;
  731. if (level_name == "floating") {
  732. level = NSFloatingWindowLevel;
  733. } else if (level_name == "torn-off-menu") {
  734. level = NSTornOffMenuWindowLevel;
  735. } else if (level_name == "modal-panel") {
  736. level = NSModalPanelWindowLevel;
  737. } else if (level_name == "main-menu") {
  738. level = NSMainMenuWindowLevel;
  739. } else if (level_name == "status") {
  740. level = NSStatusWindowLevel;
  741. } else if (level_name == "pop-up-menu") {
  742. level = NSPopUpMenuWindowLevel;
  743. } else if (level_name == "screen-saver") {
  744. level = NSScreenSaverWindowLevel;
  745. } else if (level_name == "dock") {
  746. // Deprecated by macOS, but kept for backwards compatibility
  747. level = NSDockWindowLevel;
  748. }
  749. SetWindowLevel(level + relative_level);
  750. }
  751. std::string NativeWindowMac::GetAlwaysOnTopLevel() {
  752. std::string level_name = "normal";
  753. int level = [window_ level];
  754. if (level == NSFloatingWindowLevel) {
  755. level_name = "floating";
  756. } else if (level == NSTornOffMenuWindowLevel) {
  757. level_name = "torn-off-menu";
  758. } else if (level == NSModalPanelWindowLevel) {
  759. level_name = "modal-panel";
  760. } else if (level == NSMainMenuWindowLevel) {
  761. level_name = "main-menu";
  762. } else if (level == NSStatusWindowLevel) {
  763. level_name = "status";
  764. } else if (level == NSPopUpMenuWindowLevel) {
  765. level_name = "pop-up-menu";
  766. } else if (level == NSScreenSaverWindowLevel) {
  767. level_name = "screen-saver";
  768. } else if (level == NSDockWindowLevel) {
  769. level_name = "dock";
  770. }
  771. return level_name;
  772. }
  773. void NativeWindowMac::SetWindowLevel(int unbounded_level) {
  774. int level = std::min(
  775. std::max(unbounded_level, CGWindowLevelForKey(kCGMinimumWindowLevelKey)),
  776. CGWindowLevelForKey(kCGMaximumWindowLevelKey));
  777. ui::ZOrderLevel z_order_level = level == NSNormalWindowLevel
  778. ? ui::ZOrderLevel::kNormal
  779. : ui::ZOrderLevel::kFloatingWindow;
  780. bool did_z_order_level_change = z_order_level != GetZOrderLevel();
  781. was_maximizable_ = IsMaximizable();
  782. // We need to explicitly keep the NativeWidget up to date, since it stores the
  783. // window level in a local variable, rather than reading it from the NSWindow.
  784. // Unfortunately, it results in a second setLevel call. It's not ideal, but we
  785. // don't expect this to cause any user-visible jank.
  786. widget()->SetZOrderLevel(z_order_level);
  787. [window_ setLevel:level];
  788. // Set level will make the zoom button revert to default, probably
  789. // a bug of Cocoa or macOS.
  790. SetMaximizable(was_maximizable_);
  791. // This must be notified at the very end or IsAlwaysOnTop
  792. // will not yet have been updated to reflect the new status
  793. if (did_z_order_level_change)
  794. NativeWindow::NotifyWindowAlwaysOnTopChanged();
  795. }
  796. ui::ZOrderLevel NativeWindowMac::GetZOrderLevel() {
  797. return widget()->GetZOrderLevel();
  798. }
  799. void NativeWindowMac::Center() {
  800. [window_ center];
  801. }
  802. void NativeWindowMac::Invalidate() {
  803. [window_ flushWindow];
  804. [[window_ contentView] setNeedsDisplay:YES];
  805. }
  806. void NativeWindowMac::SetTitle(const std::string& title) {
  807. [window_ setTitle:base::SysUTF8ToNSString(title)];
  808. if (buttons_proxy_)
  809. [buttons_proxy_ redraw];
  810. }
  811. std::string NativeWindowMac::GetTitle() {
  812. return base::SysNSStringToUTF8([window_ title]);
  813. }
  814. void NativeWindowMac::FlashFrame(bool flash) {
  815. if (flash) {
  816. attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
  817. } else {
  818. [NSApp cancelUserAttentionRequest:attention_request_id_];
  819. attention_request_id_ = 0;
  820. }
  821. }
  822. void NativeWindowMac::SetSkipTaskbar(bool skip) {}
  823. bool NativeWindowMac::IsExcludedFromShownWindowsMenu() {
  824. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  825. return [window isExcludedFromWindowsMenu];
  826. }
  827. void NativeWindowMac::SetExcludedFromShownWindowsMenu(bool excluded) {
  828. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  829. [window setExcludedFromWindowsMenu:excluded];
  830. }
  831. void NativeWindowMac::OnDisplayMetricsChanged(const display::Display& display,
  832. uint32_t changed_metrics) {
  833. // We only want to force screen recalibration if we're in simpleFullscreen
  834. // mode.
  835. if (!is_simple_fullscreen_)
  836. return;
  837. base::PostTask(FROM_HERE, {content::BrowserThread::UI},
  838. base::BindOnce(&NativeWindow::UpdateFrame, GetWeakPtr()));
  839. }
  840. void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
  841. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  842. if (simple_fullscreen && !is_simple_fullscreen_) {
  843. is_simple_fullscreen_ = true;
  844. // Take note of the current window size and level
  845. if (IsNormal()) {
  846. original_frame_ = [window_ frame];
  847. original_level_ = [window_ level];
  848. }
  849. simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions];
  850. simple_fullscreen_mask_ = [window styleMask];
  851. // We can simulate the pre-Lion fullscreen by auto-hiding the dock and menu
  852. // bar
  853. NSApplicationPresentationOptions options =
  854. NSApplicationPresentationAutoHideDock |
  855. NSApplicationPresentationAutoHideMenuBar;
  856. [NSApp setPresentationOptions:options];
  857. was_maximizable_ = IsMaximizable();
  858. was_movable_ = IsMovable();
  859. NSRect fullscreenFrame = [window.screen frame];
  860. // If our app has dock hidden, set the window level higher so another app's
  861. // menu bar doesn't appear on top of our fullscreen app.
  862. if ([[NSRunningApplication currentApplication] activationPolicy] !=
  863. NSApplicationActivationPolicyRegular) {
  864. window.level = NSPopUpMenuWindowLevel;
  865. }
  866. // Always hide the titlebar in simple fullscreen mode.
  867. //
  868. // Note that we must remove the NSWindowStyleMaskTitled style instead of
  869. // using the [window_ setTitleVisibility:], as the latter would leave the
  870. // window with rounded corners.
  871. SetStyleMask(false, NSWindowStyleMaskTitled);
  872. if (!window_button_visibility_.has_value()) {
  873. // Lets keep previous behaviour - hide window controls in titled
  874. // fullscreen mode when not specified otherwise.
  875. InternalSetWindowButtonVisibility(false);
  876. }
  877. [window setFrame:fullscreenFrame display:YES animate:YES];
  878. // Fullscreen windows can't be resized, minimized, maximized, or moved
  879. SetMinimizable(false);
  880. SetResizable(false);
  881. SetMaximizable(false);
  882. SetMovable(false);
  883. } else if (!simple_fullscreen && is_simple_fullscreen_) {
  884. is_simple_fullscreen_ = false;
  885. [window setFrame:original_frame_ display:YES animate:YES];
  886. window.level = original_level_;
  887. [NSApp setPresentationOptions:simple_fullscreen_options_];
  888. // Restore original style mask
  889. ScopedDisableResize disable_resize;
  890. [window_ setStyleMask:simple_fullscreen_mask_];
  891. // Restore window manipulation abilities
  892. SetMaximizable(was_maximizable_);
  893. SetMovable(was_movable_);
  894. // Restore default window controls visibility state.
  895. if (!window_button_visibility_.has_value()) {
  896. bool visibility;
  897. if (has_frame())
  898. visibility = true;
  899. else
  900. visibility = title_bar_style_ != TitleBarStyle::kNormal;
  901. InternalSetWindowButtonVisibility(visibility);
  902. }
  903. if (buttons_proxy_)
  904. [buttons_proxy_ redraw];
  905. }
  906. }
  907. bool NativeWindowMac::IsSimpleFullScreen() {
  908. return is_simple_fullscreen_;
  909. }
  910. void NativeWindowMac::SetKiosk(bool kiosk) {
  911. if (kiosk && !is_kiosk_) {
  912. kiosk_options_ = [NSApp currentSystemPresentationOptions];
  913. NSApplicationPresentationOptions options =
  914. NSApplicationPresentationHideDock |
  915. NSApplicationPresentationHideMenuBar |
  916. NSApplicationPresentationDisableAppleMenu |
  917. NSApplicationPresentationDisableProcessSwitching |
  918. NSApplicationPresentationDisableForceQuit |
  919. NSApplicationPresentationDisableSessionTermination |
  920. NSApplicationPresentationDisableHideApplication;
  921. [NSApp setPresentationOptions:options];
  922. is_kiosk_ = true;
  923. SetFullScreen(true);
  924. } else if (!kiosk && is_kiosk_) {
  925. [NSApp setPresentationOptions:kiosk_options_];
  926. is_kiosk_ = false;
  927. SetFullScreen(false);
  928. }
  929. }
  930. bool NativeWindowMac::IsKiosk() {
  931. return is_kiosk_;
  932. }
  933. void NativeWindowMac::SetBackgroundColor(SkColor color) {
  934. base::ScopedCFTypeRef<CGColorRef> cgcolor(
  935. skia::CGColorCreateFromSkColor(color));
  936. [[[window_ contentView] layer] setBackgroundColor:cgcolor];
  937. }
  938. SkColor NativeWindowMac::GetBackgroundColor() {
  939. CGColorRef color = [[[window_ contentView] layer] backgroundColor];
  940. if (!color)
  941. return SK_ColorTRANSPARENT;
  942. return skia::CGColorRefToSkColor(color);
  943. }
  944. void NativeWindowMac::SetHasShadow(bool has_shadow) {
  945. [window_ setHasShadow:has_shadow];
  946. }
  947. bool NativeWindowMac::HasShadow() {
  948. return [window_ hasShadow];
  949. }
  950. void NativeWindowMac::SetOpacity(const double opacity) {
  951. const double boundedOpacity = base::clamp(opacity, 0.0, 1.0);
  952. [window_ setAlphaValue:boundedOpacity];
  953. }
  954. double NativeWindowMac::GetOpacity() {
  955. return [window_ alphaValue];
  956. }
  957. void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
  958. [window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
  959. }
  960. std::string NativeWindowMac::GetRepresentedFilename() {
  961. return base::SysNSStringToUTF8([window_ representedFilename]);
  962. }
  963. void NativeWindowMac::SetDocumentEdited(bool edited) {
  964. [window_ setDocumentEdited:edited];
  965. if (buttons_proxy_)
  966. [buttons_proxy_ redraw];
  967. }
  968. bool NativeWindowMac::IsDocumentEdited() {
  969. return [window_ isDocumentEdited];
  970. }
  971. void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool forward) {
  972. [window_ setIgnoresMouseEvents:ignore];
  973. if (!ignore) {
  974. SetForwardMouseMessages(NO);
  975. } else {
  976. SetForwardMouseMessages(forward);
  977. }
  978. }
  979. void NativeWindowMac::SetContentProtection(bool enable) {
  980. [window_
  981. setSharingType:enable ? NSWindowSharingNone : NSWindowSharingReadOnly];
  982. }
  983. void NativeWindowMac::SetFocusable(bool focusable) {
  984. // No known way to unfocus the window if it had the focus. Here we do not
  985. // want to call Focus(false) because it moves the window to the back, i.e.
  986. // at the bottom in term of z-order.
  987. [window_ setDisableKeyOrMainWindow:!focusable];
  988. }
  989. bool NativeWindowMac::IsFocusable() {
  990. return ![window_ disableKeyOrMainWindow];
  991. }
  992. void NativeWindowMac::AddBrowserView(NativeBrowserView* view) {
  993. [CATransaction begin];
  994. [CATransaction setDisableActions:YES];
  995. if (!view) {
  996. [CATransaction commit];
  997. return;
  998. }
  999. add_browser_view(view);
  1000. if (view->GetInspectableWebContentsView()) {
  1001. auto* native_view = view->GetInspectableWebContentsView()
  1002. ->GetNativeView()
  1003. .GetNativeNSView();
  1004. [[window_ contentView] addSubview:native_view
  1005. positioned:NSWindowAbove
  1006. relativeTo:nil];
  1007. native_view.hidden = NO;
  1008. }
  1009. [CATransaction commit];
  1010. }
  1011. void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
  1012. [CATransaction begin];
  1013. [CATransaction setDisableActions:YES];
  1014. if (!view) {
  1015. [CATransaction commit];
  1016. return;
  1017. }
  1018. if (view->GetInspectableWebContentsView())
  1019. [view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
  1020. removeFromSuperview];
  1021. remove_browser_view(view);
  1022. [CATransaction commit];
  1023. }
  1024. void NativeWindowMac::SetTopBrowserView(NativeBrowserView* view) {
  1025. [CATransaction begin];
  1026. [CATransaction setDisableActions:YES];
  1027. if (!view) {
  1028. [CATransaction commit];
  1029. return;
  1030. }
  1031. remove_browser_view(view);
  1032. add_browser_view(view);
  1033. if (view->GetInspectableWebContentsView()) {
  1034. auto* native_view = view->GetInspectableWebContentsView()
  1035. ->GetNativeView()
  1036. .GetNativeNSView();
  1037. [[window_ contentView] addSubview:native_view
  1038. positioned:NSWindowAbove
  1039. relativeTo:nil];
  1040. native_view.hidden = NO;
  1041. }
  1042. [CATransaction commit];
  1043. }
  1044. void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
  1045. InternalSetParentWindow(parent, IsVisible());
  1046. }
  1047. gfx::NativeView NativeWindowMac::GetNativeView() const {
  1048. return [window_ contentView];
  1049. }
  1050. gfx::NativeWindow NativeWindowMac::GetNativeWindow() const {
  1051. return window_;
  1052. }
  1053. gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() const {
  1054. return [window_ windowNumber];
  1055. }
  1056. content::DesktopMediaID NativeWindowMac::GetDesktopMediaID() const {
  1057. auto desktop_media_id = content::DesktopMediaID(
  1058. content::DesktopMediaID::TYPE_WINDOW, GetAcceleratedWidget());
  1059. // c.f.
  1060. // https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/media/webrtc/native_desktop_media_list.cc;l=372?q=kWindowCaptureMacV2&ss=chromium
  1061. // Refs https://github.com/electron/electron/pull/30507
  1062. // TODO(deepak1556): Match upstream for `kWindowCaptureMacV2`
  1063. #if 0
  1064. if (remote_cocoa::ScopedCGWindowID::Get(desktop_media_id.id)) {
  1065. desktop_media_id.window_id = desktop_media_id.id;
  1066. }
  1067. #endif
  1068. return desktop_media_id;
  1069. }
  1070. NativeWindowHandle NativeWindowMac::GetNativeWindowHandle() const {
  1071. return [window_ contentView];
  1072. }
  1073. void NativeWindowMac::SetProgressBar(double progress,
  1074. const NativeWindow::ProgressState state) {
  1075. NSDockTile* dock_tile = [NSApp dockTile];
  1076. // Sometimes macOS would install a default contentView for dock, we must
  1077. // verify whether NSProgressIndicator has been installed.
  1078. bool first_time = !dock_tile.contentView ||
  1079. [[dock_tile.contentView subviews] count] == 0 ||
  1080. ![[[dock_tile.contentView subviews] lastObject]
  1081. isKindOfClass:[NSProgressIndicator class]];
  1082. // For the first time API invoked, we need to create a ContentView in
  1083. // DockTile.
  1084. if (first_time) {
  1085. NSImageView* image_view = [[[NSImageView alloc] init] autorelease];
  1086. [image_view setImage:[NSApp applicationIconImage]];
  1087. [dock_tile setContentView:image_view];
  1088. NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0);
  1089. NSProgressIndicator* progress_indicator =
  1090. [[[ElectronProgressBar alloc] initWithFrame:frame] autorelease];
  1091. [progress_indicator setStyle:NSProgressIndicatorBarStyle];
  1092. [progress_indicator setIndeterminate:NO];
  1093. [progress_indicator setBezeled:YES];
  1094. [progress_indicator setMinValue:0];
  1095. [progress_indicator setMaxValue:1];
  1096. [progress_indicator setHidden:NO];
  1097. [dock_tile.contentView addSubview:progress_indicator];
  1098. }
  1099. NSProgressIndicator* progress_indicator = static_cast<NSProgressIndicator*>(
  1100. [[[dock_tile contentView] subviews] lastObject]);
  1101. if (progress < 0) {
  1102. [progress_indicator setHidden:YES];
  1103. } else if (progress > 1) {
  1104. [progress_indicator setHidden:NO];
  1105. [progress_indicator setIndeterminate:YES];
  1106. [progress_indicator setDoubleValue:1];
  1107. } else {
  1108. [progress_indicator setHidden:NO];
  1109. [progress_indicator setDoubleValue:progress];
  1110. }
  1111. [dock_tile display];
  1112. }
  1113. void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
  1114. const std::string& description) {}
  1115. void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible,
  1116. bool visibleOnFullScreen,
  1117. bool skipTransformProcessType) {
  1118. // In order for NSWindows to be visible on fullscreen we need to functionally
  1119. // mimic app.dock.hide() since Apple changed the underlying functionality of
  1120. // NSWindows starting with 10.14 to disallow NSWindows from floating on top of
  1121. // fullscreen apps.
  1122. if (!skipTransformProcessType) {
  1123. ProcessSerialNumber psn = {0, kCurrentProcess};
  1124. if (visibleOnFullScreen) {
  1125. [window_ setCanHide:NO];
  1126. TransformProcessType(&psn, kProcessTransformToUIElementApplication);
  1127. } else {
  1128. [window_ setCanHide:YES];
  1129. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  1130. }
  1131. }
  1132. SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
  1133. SetCollectionBehavior(visibleOnFullScreen,
  1134. NSWindowCollectionBehaviorFullScreenAuxiliary);
  1135. }
  1136. bool NativeWindowMac::IsVisibleOnAllWorkspaces() {
  1137. NSUInteger collectionBehavior = [window_ collectionBehavior];
  1138. return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces;
  1139. }
  1140. void NativeWindowMac::SetAutoHideCursor(bool auto_hide) {
  1141. [window_ setDisableAutoHideCursor:!auto_hide];
  1142. }
  1143. void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
  1144. NSVisualEffectView* vibrantView = [window_ vibrantView];
  1145. if (vibrantView != nil && !vibrancy_type_.empty()) {
  1146. const bool no_rounded_corner =
  1147. !([window_ styleMask] & NSWindowStyleMaskTitled);
  1148. if (!has_frame() && !is_modal() && !no_rounded_corner) {
  1149. CGFloat radius;
  1150. if (fullscreen) {
  1151. radius = 0.0f;
  1152. } else if (@available(macOS 11.0, *)) {
  1153. radius = 9.0f;
  1154. } else {
  1155. // Smaller corner radius on versions prior to Big Sur.
  1156. radius = 5.0f;
  1157. }
  1158. CGFloat dimension = 2 * radius + 1;
  1159. NSSize size = NSMakeSize(dimension, dimension);
  1160. NSImage* maskImage = [NSImage imageWithSize:size
  1161. flipped:NO
  1162. drawingHandler:^BOOL(NSRect rect) {
  1163. NSBezierPath* bezierPath = [NSBezierPath
  1164. bezierPathWithRoundedRect:rect
  1165. xRadius:radius
  1166. yRadius:radius];
  1167. [[NSColor blackColor] set];
  1168. [bezierPath fill];
  1169. return YES;
  1170. }];
  1171. [maskImage setCapInsets:NSEdgeInsetsMake(radius, radius, radius, radius)];
  1172. [maskImage setResizingMode:NSImageResizingModeStretch];
  1173. [vibrantView setMaskImage:maskImage];
  1174. [window_ setCornerMask:maskImage];
  1175. }
  1176. }
  1177. }
  1178. void NativeWindowMac::SetVibrancy(const std::string& type) {
  1179. NSVisualEffectView* vibrantView = [window_ vibrantView];
  1180. if (type.empty()) {
  1181. if (vibrantView == nil)
  1182. return;
  1183. [vibrantView removeFromSuperview];
  1184. [window_ setVibrantView:nil];
  1185. return;
  1186. }
  1187. std::string dep_warn = " has been deprecated and removed as of macOS 10.15.";
  1188. node::Environment* env =
  1189. node::Environment::GetCurrent(JavascriptEnvironment::GetIsolate());
  1190. NSVisualEffectMaterial vibrancyType{};
  1191. if (type == "appearance-based") {
  1192. EmitWarning(env, "NSVisualEffectMaterialAppearanceBased" + dep_warn,
  1193. "electron");
  1194. vibrancyType = NSVisualEffectMaterialAppearanceBased;
  1195. } else if (type == "light") {
  1196. EmitWarning(env, "NSVisualEffectMaterialLight" + dep_warn, "electron");
  1197. vibrancyType = NSVisualEffectMaterialLight;
  1198. } else if (type == "dark") {
  1199. EmitWarning(env, "NSVisualEffectMaterialDark" + dep_warn, "electron");
  1200. vibrancyType = NSVisualEffectMaterialDark;
  1201. } else if (type == "titlebar") {
  1202. vibrancyType = NSVisualEffectMaterialTitlebar;
  1203. }
  1204. if (@available(macOS 10.11, *)) {
  1205. if (type == "selection") {
  1206. vibrancyType = NSVisualEffectMaterialSelection;
  1207. } else if (type == "menu") {
  1208. vibrancyType = NSVisualEffectMaterialMenu;
  1209. } else if (type == "popover") {
  1210. vibrancyType = NSVisualEffectMaterialPopover;
  1211. } else if (type == "sidebar") {
  1212. vibrancyType = NSVisualEffectMaterialSidebar;
  1213. } else if (type == "medium-light") {
  1214. EmitWarning(env, "NSVisualEffectMaterialMediumLight" + dep_warn,
  1215. "electron");
  1216. vibrancyType = NSVisualEffectMaterialMediumLight;
  1217. } else if (type == "ultra-dark") {
  1218. EmitWarning(env, "NSVisualEffectMaterialUltraDark" + dep_warn,
  1219. "electron");
  1220. vibrancyType = NSVisualEffectMaterialUltraDark;
  1221. }
  1222. }
  1223. if (@available(macOS 10.14, *)) {
  1224. if (type == "header") {
  1225. vibrancyType = NSVisualEffectMaterialHeaderView;
  1226. } else if (type == "sheet") {
  1227. vibrancyType = NSVisualEffectMaterialSheet;
  1228. } else if (type == "window") {
  1229. vibrancyType = NSVisualEffectMaterialWindowBackground;
  1230. } else if (type == "hud") {
  1231. vibrancyType = NSVisualEffectMaterialHUDWindow;
  1232. } else if (type == "fullscreen-ui") {
  1233. vibrancyType = NSVisualEffectMaterialFullScreenUI;
  1234. } else if (type == "tooltip") {
  1235. vibrancyType = NSVisualEffectMaterialToolTip;
  1236. } else if (type == "content") {
  1237. vibrancyType = NSVisualEffectMaterialContentBackground;
  1238. } else if (type == "under-window") {
  1239. vibrancyType = NSVisualEffectMaterialUnderWindowBackground;
  1240. } else if (type == "under-page") {
  1241. vibrancyType = NSVisualEffectMaterialUnderPageBackground;
  1242. }
  1243. }
  1244. if (vibrancyType) {
  1245. vibrancy_type_ = type;
  1246. if (vibrantView == nil) {
  1247. vibrantView = [[[NSVisualEffectView alloc]
  1248. initWithFrame:[[window_ contentView] bounds]] autorelease];
  1249. [window_ setVibrantView:vibrantView];
  1250. [vibrantView
  1251. setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1252. [vibrantView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
  1253. if (visual_effect_state_ == VisualEffectState::kActive) {
  1254. [vibrantView setState:NSVisualEffectStateActive];
  1255. } else if (visual_effect_state_ == VisualEffectState::kInactive) {
  1256. [vibrantView setState:NSVisualEffectStateInactive];
  1257. } else {
  1258. [vibrantView setState:NSVisualEffectStateFollowsWindowActiveState];
  1259. }
  1260. [[window_ contentView] addSubview:vibrantView
  1261. positioned:NSWindowBelow
  1262. relativeTo:nil];
  1263. UpdateVibrancyRadii(IsFullscreen());
  1264. }
  1265. [vibrantView setMaterial:vibrancyType];
  1266. }
  1267. }
  1268. void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
  1269. window_button_visibility_ = visible;
  1270. // The visibility of window buttons are managed by |buttons_proxy_| if the
  1271. // style is customButtonsOnHover.
  1272. if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
  1273. [buttons_proxy_ setVisible:visible];
  1274. else
  1275. InternalSetWindowButtonVisibility(visible);
  1276. NotifyLayoutWindowControlsOverlay();
  1277. }
  1278. bool NativeWindowMac::GetWindowButtonVisibility() const {
  1279. return ![window_ standardWindowButton:NSWindowZoomButton].hidden ||
  1280. ![window_ standardWindowButton:NSWindowMiniaturizeButton].hidden ||
  1281. ![window_ standardWindowButton:NSWindowCloseButton].hidden;
  1282. }
  1283. void NativeWindowMac::SetTrafficLightPosition(
  1284. absl::optional<gfx::Point> position) {
  1285. traffic_light_position_ = std::move(position);
  1286. if (buttons_proxy_) {
  1287. [buttons_proxy_ setMargin:traffic_light_position_];
  1288. NotifyLayoutWindowControlsOverlay();
  1289. }
  1290. }
  1291. absl::optional<gfx::Point> NativeWindowMac::GetTrafficLightPosition() const {
  1292. return traffic_light_position_;
  1293. }
  1294. void NativeWindowMac::RedrawTrafficLights() {
  1295. if (buttons_proxy_ && !IsFullscreen())
  1296. [buttons_proxy_ redraw];
  1297. }
  1298. // In simpleFullScreen mode, update the frame for new bounds.
  1299. void NativeWindowMac::UpdateFrame() {
  1300. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  1301. NSRect fullscreenFrame = [window.screen frame];
  1302. [window setFrame:fullscreenFrame display:YES animate:YES];
  1303. }
  1304. void NativeWindowMac::SetTouchBar(
  1305. std::vector<gin_helper::PersistentDictionary> items) {
  1306. if (@available(macOS 10.12.2, *)) {
  1307. touch_bar_.reset([[ElectronTouchBar alloc]
  1308. initWithDelegate:window_delegate_.get()
  1309. window:this
  1310. settings:std::move(items)]);
  1311. [window_ setTouchBar:nil];
  1312. }
  1313. }
  1314. void NativeWindowMac::RefreshTouchBarItem(const std::string& item_id) {
  1315. if (@available(macOS 10.12.2, *)) {
  1316. if (touch_bar_ && [window_ touchBar])
  1317. [touch_bar_ refreshTouchBarItem:[window_ touchBar] id:item_id];
  1318. }
  1319. }
  1320. void NativeWindowMac::SetEscapeTouchBarItem(
  1321. gin_helper::PersistentDictionary item) {
  1322. if (@available(macOS 10.12.2, *)) {
  1323. if (touch_bar_ && [window_ touchBar])
  1324. [touch_bar_ setEscapeTouchBarItem:std::move(item)
  1325. forTouchBar:[window_ touchBar]];
  1326. }
  1327. }
  1328. void NativeWindowMac::SelectPreviousTab() {
  1329. if (@available(macOS 10.12, *)) {
  1330. [window_ selectPreviousTab:nil];
  1331. }
  1332. }
  1333. void NativeWindowMac::SelectNextTab() {
  1334. if (@available(macOS 10.12, *)) {
  1335. [window_ selectNextTab:nil];
  1336. }
  1337. }
  1338. void NativeWindowMac::MergeAllWindows() {
  1339. if (@available(macOS 10.12, *)) {
  1340. [window_ mergeAllWindows:nil];
  1341. }
  1342. }
  1343. void NativeWindowMac::MoveTabToNewWindow() {
  1344. if (@available(macOS 10.12, *)) {
  1345. [window_ moveTabToNewWindow:nil];
  1346. }
  1347. }
  1348. void NativeWindowMac::ToggleTabBar() {
  1349. if (@available(macOS 10.12, *)) {
  1350. [window_ toggleTabBar:nil];
  1351. }
  1352. }
  1353. bool NativeWindowMac::AddTabbedWindow(NativeWindow* window) {
  1354. if (window_ == window->GetNativeWindow().GetNativeNSWindow()) {
  1355. return false;
  1356. } else {
  1357. if (@available(macOS 10.12, *))
  1358. [window_ addTabbedWindow:window->GetNativeWindow().GetNativeNSWindow()
  1359. ordered:NSWindowAbove];
  1360. }
  1361. return true;
  1362. }
  1363. void NativeWindowMac::SetAspectRatio(double aspect_ratio,
  1364. const gfx::Size& extra_size) {
  1365. NativeWindow::SetAspectRatio(aspect_ratio, extra_size);
  1366. // Reset the behaviour to default if aspect_ratio is set to 0 or less.
  1367. if (aspect_ratio > 0.0) {
  1368. NSSize aspect_ratio_size = NSMakeSize(aspect_ratio, 1.0);
  1369. if (has_frame())
  1370. [window_ setContentAspectRatio:aspect_ratio_size];
  1371. else
  1372. [window_ setAspectRatio:aspect_ratio_size];
  1373. } else {
  1374. [window_ setResizeIncrements:NSMakeSize(1.0, 1.0)];
  1375. }
  1376. }
  1377. void NativeWindowMac::PreviewFile(const std::string& path,
  1378. const std::string& display_name) {
  1379. preview_item_.reset([[ElectronPreviewItem alloc]
  1380. initWithURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(path)]
  1381. title:base::SysUTF8ToNSString(display_name)]);
  1382. [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
  1383. }
  1384. void NativeWindowMac::CloseFilePreview() {
  1385. if ([QLPreviewPanel sharedPreviewPanelExists]) {
  1386. [[QLPreviewPanel sharedPreviewPanel] close];
  1387. }
  1388. }
  1389. gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
  1390. const gfx::Rect& bounds) const {
  1391. if (has_frame()) {
  1392. gfx::Rect window_bounds(
  1393. [window_ frameRectForContentRect:bounds.ToCGRect()]);
  1394. int frame_height = window_bounds.height() - bounds.height();
  1395. window_bounds.set_y(window_bounds.y() - frame_height);
  1396. return window_bounds;
  1397. } else {
  1398. return bounds;
  1399. }
  1400. }
  1401. gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
  1402. const gfx::Rect& bounds) const {
  1403. if (has_frame()) {
  1404. gfx::Rect content_bounds(
  1405. [window_ contentRectForFrameRect:bounds.ToCGRect()]);
  1406. int frame_height = bounds.height() - content_bounds.height();
  1407. content_bounds.set_y(content_bounds.y() + frame_height);
  1408. return content_bounds;
  1409. } else {
  1410. return bounds;
  1411. }
  1412. }
  1413. void NativeWindowMac::NotifyWindowEnterFullScreen() {
  1414. NativeWindow::NotifyWindowEnterFullScreen();
  1415. // Restore the window title under fullscreen mode.
  1416. if (buttons_proxy_)
  1417. [window_ setTitleVisibility:NSWindowTitleVisible];
  1418. }
  1419. void NativeWindowMac::NotifyWindowLeaveFullScreen() {
  1420. NativeWindow::NotifyWindowLeaveFullScreen();
  1421. // Restore window buttons.
  1422. if (buttons_proxy_ && window_button_visibility_.value_or(true)) {
  1423. [buttons_proxy_ redraw];
  1424. [buttons_proxy_ setVisible:YES];
  1425. }
  1426. }
  1427. void NativeWindowMac::NotifyWindowWillEnterFullScreen() {
  1428. UpdateVibrancyRadii(true);
  1429. }
  1430. void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
  1431. if (buttons_proxy_) {
  1432. // Hide window title when leaving fullscreen.
  1433. [window_ setTitleVisibility:NSWindowTitleHidden];
  1434. // Hide the container otherwise traffic light buttons jump.
  1435. [buttons_proxy_ setVisible:NO];
  1436. }
  1437. UpdateVibrancyRadii(false);
  1438. }
  1439. void NativeWindowMac::SetActive(bool is_key) {
  1440. is_active_ = is_key;
  1441. }
  1442. bool NativeWindowMac::IsActive() const {
  1443. return is_active_;
  1444. }
  1445. void NativeWindowMac::Cleanup() {
  1446. DCHECK(!IsClosed());
  1447. ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
  1448. display::Screen::GetScreen()->RemoveObserver(this);
  1449. if (wheel_event_monitor_) {
  1450. [NSEvent removeMonitor:wheel_event_monitor_];
  1451. wheel_event_monitor_ = nil;
  1452. }
  1453. }
  1454. void NativeWindowMac::OverrideNSWindowContentView() {
  1455. // When using `views::Widget` to hold WebContents, Chromium would use
  1456. // `BridgedContentView` as content view, which does not support draggable
  1457. // regions. In order to make draggable regions work, we have to replace the
  1458. // content view with a simple NSView.
  1459. if (has_frame()) {
  1460. container_view_.reset(
  1461. [[ElectronAdaptedContentView alloc] initWithShell:this]);
  1462. } else {
  1463. container_view_.reset([[FullSizeContentView alloc] init]);
  1464. [container_view_ setFrame:[[[window_ contentView] superview] bounds]];
  1465. }
  1466. [container_view_
  1467. setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1468. [window_ setContentView:container_view_];
  1469. AddContentViewLayers();
  1470. }
  1471. void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
  1472. // Changing the styleMask of a frameless windows causes it to change size so
  1473. // we explicitly disable resizing while setting it.
  1474. ScopedDisableResize disable_resize;
  1475. if (on)
  1476. [window_ setStyleMask:[window_ styleMask] | flag];
  1477. else
  1478. [window_ setStyleMask:[window_ styleMask] & (~flag)];
  1479. // Change style mask will make the zoom button revert to default, probably
  1480. // a bug of Cocoa or macOS.
  1481. SetMaximizable(maximizable_);
  1482. }
  1483. void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
  1484. if (on)
  1485. [window_ setCollectionBehavior:[window_ collectionBehavior] | flag];
  1486. else
  1487. [window_ setCollectionBehavior:[window_ collectionBehavior] & (~flag)];
  1488. // Change collectionBehavior will make the zoom button revert to default,
  1489. // probably a bug of Cocoa or macOS.
  1490. SetMaximizable(maximizable_);
  1491. }
  1492. views::View* NativeWindowMac::GetContentsView() {
  1493. return root_view_.get();
  1494. }
  1495. bool NativeWindowMac::CanMaximize() const {
  1496. return maximizable_;
  1497. }
  1498. void NativeWindowMac::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
  1499. base::PostTask(
  1500. FROM_HERE, {content::BrowserThread::UI},
  1501. base::BindOnce(&NativeWindow::RedrawTrafficLights, GetWeakPtr()));
  1502. }
  1503. void NativeWindowMac::AddContentViewLayers() {
  1504. // Make sure the bottom corner is rounded for non-modal windows:
  1505. // http://crbug.com/396264.
  1506. if (!is_modal()) {
  1507. // For normal window, we need to explicitly set layer for contentView to
  1508. // make setBackgroundColor work correctly.
  1509. // There is no need to do so for frameless window, and doing so would make
  1510. // titleBarStyle stop working.
  1511. if (has_frame()) {
  1512. base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
  1513. [background_layer
  1514. setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
  1515. [[window_ contentView] setLayer:background_layer];
  1516. }
  1517. [[window_ contentView] setWantsLayer:YES];
  1518. }
  1519. if (!has_frame()) {
  1520. // In OSX 10.10, adding subviews to the root view for the NSView hierarchy
  1521. // produces warnings. To eliminate the warnings, we resize the contentView
  1522. // to fill the window, and add subviews to that.
  1523. // http://crbug.com/380412
  1524. if (!original_set_frame_size) {
  1525. Class cl = [[window_ contentView] class];
  1526. original_set_frame_size = class_replaceMethod(
  1527. cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}");
  1528. original_view_did_move_to_superview =
  1529. class_replaceMethod(cl, @selector(viewDidMoveToSuperview),
  1530. (IMP)ViewDidMoveToSuperview, "v@:");
  1531. [[window_ contentView] viewDidMoveToWindow];
  1532. }
  1533. }
  1534. }
  1535. void NativeWindowMac::InternalSetWindowButtonVisibility(bool visible) {
  1536. [[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
  1537. [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
  1538. [[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
  1539. }
  1540. void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,
  1541. bool attach) {
  1542. if (is_modal())
  1543. return;
  1544. NativeWindow::SetParentWindow(parent);
  1545. // Do not remove/add if we are already properly attached.
  1546. if (attach && parent &&
  1547. [window_ parentWindow] == parent->GetNativeWindow().GetNativeNSWindow())
  1548. return;
  1549. // Remove current parent window.
  1550. if ([window_ parentWindow])
  1551. [[window_ parentWindow] removeChildWindow:window_];
  1552. // Set new parent window.
  1553. // Note that this method will force the window to become visible.
  1554. if (parent && attach) {
  1555. // Attaching a window as a child window resets its window level, so
  1556. // save and restore it afterwards.
  1557. NSInteger level = window_.level;
  1558. [parent->GetNativeWindow().GetNativeNSWindow()
  1559. addChildWindow:window_
  1560. ordered:NSWindowAbove];
  1561. [window_ setLevel:level];
  1562. }
  1563. }
  1564. void NativeWindowMac::SetForwardMouseMessages(bool forward) {
  1565. [window_ setAcceptsMouseMovedEvents:forward];
  1566. }
  1567. gfx::Rect NativeWindowMac::GetWindowControlsOverlayRect() {
  1568. if (titlebar_overlay_ && buttons_proxy_ &&
  1569. window_button_visibility_.value_or(true)) {
  1570. NSRect buttons = [buttons_proxy_ getButtonsContainerBounds];
  1571. gfx::Rect overlay;
  1572. overlay.set_width(GetContentSize().width() - NSWidth(buttons));
  1573. if ([buttons_proxy_ useCustomHeight]) {
  1574. overlay.set_height(titlebar_overlay_height());
  1575. } else {
  1576. overlay.set_height(NSHeight(buttons));
  1577. }
  1578. if (!base::i18n::IsRTL())
  1579. overlay.set_x(NSMaxX(buttons));
  1580. return overlay;
  1581. }
  1582. return gfx::Rect();
  1583. }
  1584. // static
  1585. NativeWindow* NativeWindow::Create(const gin_helper::Dictionary& options,
  1586. NativeWindow* parent) {
  1587. return new NativeWindowMac(options, parent);
  1588. }
  1589. } // namespace electron