native_window_mac.mm 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  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/mac/mac_util.h"
  12. #include "base/mac/scoped_cftyperef.h"
  13. #include "base/numerics/ranges.h"
  14. #include "base/strings/sys_string_conversions.h"
  15. #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
  16. #include "content/public/browser/browser_accessibility_state.h"
  17. #include "native_mate/dictionary.h"
  18. #include "shell/browser/native_browser_view_mac.h"
  19. #include "shell/browser/ui/cocoa/atom_native_widget_mac.h"
  20. #include "shell/browser/ui/cocoa/atom_ns_window.h"
  21. #include "shell/browser/ui/cocoa/atom_ns_window_delegate.h"
  22. #include "shell/browser/ui/cocoa/atom_preview_item.h"
  23. #include "shell/browser/ui/cocoa/atom_touch_bar.h"
  24. #include "shell/browser/ui/cocoa/root_view_mac.h"
  25. #include "shell/browser/ui/inspectable_web_contents.h"
  26. #include "shell/browser/ui/inspectable_web_contents_view.h"
  27. #include "shell/browser/window_list.h"
  28. #include "shell/common/deprecate_util.h"
  29. #include "shell/common/options_switches.h"
  30. #include "skia/ext/skia_utils_mac.h"
  31. #include "ui/gfx/skia_util.h"
  32. #include "ui/gl/gpu_switching_manager.h"
  33. #include "ui/views/background.h"
  34. #include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
  35. #include "ui/views/widget/widget.h"
  36. // This view would inform Chromium to resize the hosted views::View.
  37. //
  38. // The overrided methods should behave the same with BridgedContentView.
  39. @interface ElectronAdapatedContentView : NSView {
  40. @private
  41. views::NativeWidgetMacNSWindowHost* bridge_host_;
  42. }
  43. @end
  44. @implementation ElectronAdapatedContentView
  45. - (id)initWithShell:(electron::NativeWindowMac*)shell {
  46. if ((self = [self init])) {
  47. bridge_host_ = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(
  48. shell->GetNativeWindow());
  49. }
  50. return self;
  51. }
  52. - (void)viewDidMoveToWindow {
  53. // When this view is added to a window, AppKit calls setFrameSize before it is
  54. // added to the window, so the behavior in setFrameSize is not triggered.
  55. NSWindow* window = [self window];
  56. if (window)
  57. [self setFrameSize:NSZeroSize];
  58. }
  59. - (void)setFrameSize:(NSSize)newSize {
  60. // The size passed in here does not always use
  61. // -[NSWindow contentRectForFrameRect]. The following ensures that the
  62. // contentView for a frameless window can extend over the titlebar of the new
  63. // window containing it, since AppKit requires a titlebar to give frameless
  64. // windows correct shadows and rounded corners.
  65. NSWindow* window = [self window];
  66. if (window && [window contentView] == self) {
  67. newSize = [window contentRectForFrameRect:[window frame]].size;
  68. // Ensure that the window geometry be updated on the host side before the
  69. // view size is updated.
  70. bridge_host_->GetInProcessNSWindowBridge()->UpdateWindowGeometry();
  71. }
  72. [super setFrameSize:newSize];
  73. // The OnViewSizeChanged is marked private in derived class.
  74. static_cast<remote_cocoa::mojom::NativeWidgetNSWindowHost*>(bridge_host_)
  75. ->OnViewSizeChanged(gfx::Size(newSize.width, newSize.height));
  76. }
  77. @end
  78. // This view always takes the size of its superview. It is intended to be used
  79. // as a NSWindow's contentView. It is needed because NSWindow's implementation
  80. // explicitly resizes the contentView at inopportune times.
  81. @interface FullSizeContentView : NSView
  82. @end
  83. @implementation FullSizeContentView
  84. // This method is directly called by NSWindow during a window resize on OSX
  85. // 10.10.0, beta 2. We must override it to prevent the content view from
  86. // shrinking.
  87. - (void)setFrameSize:(NSSize)size {
  88. if ([self superview])
  89. size = [[self superview] bounds].size;
  90. [super setFrameSize:size];
  91. }
  92. // The contentView gets moved around during certain full-screen operations.
  93. // This is less than ideal, and should eventually be removed.
  94. - (void)viewDidMoveToSuperview {
  95. [self setFrame:[[self superview] bounds]];
  96. }
  97. @end
  98. // Custom Quit, Minimize and Full Screen button container for frameless
  99. // windows.
  100. @interface CustomWindowButtonView : NSView {
  101. @private
  102. BOOL mouse_inside_;
  103. }
  104. @end
  105. @implementation CustomWindowButtonView
  106. - (id)initWithFrame:(NSRect)frame {
  107. self = [super initWithFrame:frame];
  108. NSButton* close_button =
  109. [NSWindow standardWindowButton:NSWindowCloseButton
  110. forStyleMask:NSWindowStyleMaskTitled];
  111. [close_button setTag:1];
  112. NSButton* miniaturize_button =
  113. [NSWindow standardWindowButton:NSWindowMiniaturizeButton
  114. forStyleMask:NSWindowStyleMaskTitled];
  115. [miniaturize_button setTag:2];
  116. CGFloat x = 0;
  117. const CGFloat space_between = 20;
  118. [close_button setFrameOrigin:NSMakePoint(x, 0)];
  119. x += space_between;
  120. [self addSubview:close_button];
  121. [miniaturize_button setFrameOrigin:NSMakePoint(x, 0)];
  122. x += space_between;
  123. [self addSubview:miniaturize_button];
  124. const auto last_button_frame = miniaturize_button.frame;
  125. [self setFrameSize:NSMakeSize(last_button_frame.origin.x +
  126. last_button_frame.size.width,
  127. last_button_frame.size.height)];
  128. mouse_inside_ = NO;
  129. [self setNeedsDisplayForButtons];
  130. return self;
  131. }
  132. - (void)viewDidMoveToWindow {
  133. if (!self.window) {
  134. return;
  135. }
  136. // Stay in upper left corner.
  137. const CGFloat top_margin = 3;
  138. const CGFloat left_margin = 7;
  139. [self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
  140. [self setFrameOrigin:NSMakePoint(left_margin, self.window.frame.size.height -
  141. self.frame.size.height -
  142. top_margin)];
  143. }
  144. - (BOOL)_mouseInGroup:(NSButton*)button {
  145. return mouse_inside_;
  146. }
  147. - (void)updateTrackingAreas {
  148. auto tracking_area = [[[NSTrackingArea alloc]
  149. initWithRect:NSZeroRect
  150. options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
  151. NSTrackingInVisibleRect
  152. owner:self
  153. userInfo:nil] autorelease];
  154. [self addTrackingArea:tracking_area];
  155. }
  156. - (void)mouseEntered:(NSEvent*)event {
  157. [super mouseEntered:event];
  158. mouse_inside_ = YES;
  159. [self setNeedsDisplayForButtons];
  160. }
  161. - (void)mouseExited:(NSEvent*)event {
  162. [super mouseExited:event];
  163. mouse_inside_ = NO;
  164. [self setNeedsDisplayForButtons];
  165. }
  166. - (void)setNeedsDisplayForButtons {
  167. for (NSView* subview in self.subviews) {
  168. [subview setHidden:!mouse_inside_];
  169. [subview setNeedsDisplay:YES];
  170. }
  171. }
  172. @end
  173. @interface AtomProgressBar : NSProgressIndicator
  174. @end
  175. @implementation AtomProgressBar
  176. - (void)drawRect:(NSRect)dirtyRect {
  177. if (self.style != NSProgressIndicatorBarStyle)
  178. return;
  179. // Draw edges of rounded rect.
  180. NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
  181. CGFloat radius = rect.size.height / 2;
  182. NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
  183. xRadius:radius
  184. yRadius:radius];
  185. [bezier_path setLineWidth:2.0];
  186. [[NSColor grayColor] set];
  187. [bezier_path stroke];
  188. // Fill the rounded rect.
  189. rect = NSInsetRect(rect, 2.0, 2.0);
  190. radius = rect.size.height / 2;
  191. bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect
  192. xRadius:radius
  193. yRadius:radius];
  194. [bezier_path setLineWidth:1.0];
  195. [bezier_path addClip];
  196. // Calculate the progress width.
  197. rect.size.width =
  198. floor(rect.size.width * ([self doubleValue] / [self maxValue]));
  199. // Fill the progress bar with color blue.
  200. [[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1 alpha:1] set];
  201. NSRectFill(rect);
  202. }
  203. @end
  204. namespace mate {
  205. template <>
  206. struct Converter<electron::NativeWindowMac::TitleBarStyle> {
  207. static bool FromV8(v8::Isolate* isolate,
  208. v8::Handle<v8::Value> val,
  209. electron::NativeWindowMac::TitleBarStyle* out) {
  210. using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
  211. std::string title_bar_style;
  212. if (!ConvertFromV8(isolate, val, &title_bar_style))
  213. return false;
  214. if (title_bar_style == "hidden") {
  215. *out = TitleBarStyle::HIDDEN;
  216. } else if (title_bar_style == "hiddenInset") {
  217. *out = TitleBarStyle::HIDDEN_INSET;
  218. } else if (title_bar_style == "customButtonsOnHover") {
  219. *out = TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER;
  220. } else {
  221. return false;
  222. }
  223. return true;
  224. }
  225. };
  226. } // namespace mate
  227. namespace electron {
  228. namespace {
  229. bool IsFramelessWindow(NSView* view) {
  230. NSWindow* nswindow = [view window];
  231. if (![nswindow respondsToSelector:@selector(shell)])
  232. return false;
  233. NativeWindow* window = [static_cast<AtomNSWindow*>(nswindow) shell];
  234. return window && !window->has_frame();
  235. }
  236. IMP original_set_frame_size = nullptr;
  237. IMP original_view_did_move_to_superview = nullptr;
  238. // This method is directly called by NSWindow during a window resize on OSX
  239. // 10.10.0, beta 2. We must override it to prevent the content view from
  240. // shrinking.
  241. void SetFrameSize(NSView* self, SEL _cmd, NSSize size) {
  242. if (!IsFramelessWindow(self)) {
  243. auto original =
  244. reinterpret_cast<decltype(&SetFrameSize)>(original_set_frame_size);
  245. return original(self, _cmd, size);
  246. }
  247. // For frameless window, resize the view to cover full window.
  248. if ([self superview])
  249. size = [[self superview] bounds].size;
  250. auto super_impl = reinterpret_cast<decltype(&SetFrameSize)>(
  251. [[self superclass] instanceMethodForSelector:_cmd]);
  252. super_impl(self, _cmd, size);
  253. }
  254. // The contentView gets moved around during certain full-screen operations.
  255. // This is less than ideal, and should eventually be removed.
  256. void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
  257. if (!IsFramelessWindow(self)) {
  258. // [BridgedContentView viewDidMoveToSuperview];
  259. auto original = reinterpret_cast<decltype(&ViewDidMoveToSuperview)>(
  260. original_view_did_move_to_superview);
  261. if (original)
  262. original(self, _cmd);
  263. return;
  264. }
  265. [self setFrame:[[self superview] bounds]];
  266. }
  267. } // namespace
  268. NativeWindowMac::NativeWindowMac(const mate::Dictionary& options,
  269. NativeWindow* parent)
  270. : NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
  271. int width = 800, height = 600;
  272. options.Get(options::kWidth, &width);
  273. options.Get(options::kHeight, &height);
  274. NSRect main_screen_rect = [[[NSScreen screens] firstObject] frame];
  275. gfx::Rect bounds(round((NSWidth(main_screen_rect) - width) / 2),
  276. round((NSHeight(main_screen_rect) - height) / 2), width,
  277. height);
  278. options.Get(options::kResizable, &resizable_);
  279. options.Get(options::kTitleBarStyle, &title_bar_style_);
  280. options.Get(options::kZoomToPageWidth, &zoom_to_page_width_);
  281. options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_);
  282. options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_);
  283. bool minimizable = true;
  284. options.Get(options::kMinimizable, &minimizable);
  285. bool maximizable = true;
  286. options.Get(options::kMaximizable, &maximizable);
  287. bool closable = true;
  288. options.Get(options::kClosable, &closable);
  289. std::string tabbingIdentifier;
  290. options.Get(options::kTabbingIdentifier, &tabbingIdentifier);
  291. std::string windowType;
  292. options.Get(options::kType, &windowType);
  293. bool useStandardWindow = true;
  294. // eventually deprecate separate "standardWindow" option in favor of
  295. // standard / textured window types
  296. options.Get(options::kStandardWindow, &useStandardWindow);
  297. if (windowType == "textured") {
  298. useStandardWindow = false;
  299. }
  300. NSUInteger styleMask = NSWindowStyleMaskTitled;
  301. bool customOnHover =
  302. title_bar_style_ == TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER;
  303. if (customOnHover && (!useStandardWindow || transparent() || !has_frame()))
  304. styleMask = NSWindowStyleMaskFullSizeContentView;
  305. if (minimizable)
  306. styleMask |= NSMiniaturizableWindowMask;
  307. if (closable)
  308. styleMask |= NSWindowStyleMaskClosable;
  309. if (resizable_)
  310. styleMask |= NSResizableWindowMask;
  311. // The window without titlebar is treated the same with frameless window.
  312. if (title_bar_style_ != TitleBarStyle::NORMAL)
  313. set_has_frame(false);
  314. if (!useStandardWindow || transparent() || !has_frame())
  315. styleMask |= NSTexturedBackgroundWindowMask;
  316. // Create views::Widget and assign window_ with it.
  317. // TODO(zcbenz): Get rid of the window_ in future.
  318. views::Widget::InitParams params;
  319. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  320. params.bounds = bounds;
  321. params.delegate = this;
  322. params.type = views::Widget::InitParams::TYPE_WINDOW;
  323. params.native_widget = new AtomNativeWidgetMac(this, styleMask, widget());
  324. widget()->Init(std::move(params));
  325. window_ = static_cast<AtomNSWindow*>(
  326. widget()->GetNativeWindow().GetNativeNSWindow());
  327. [window_ setEnableLargerThanScreen:enable_larger_than_screen()];
  328. window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]);
  329. [window_ setDelegate:window_delegate_];
  330. // Only use native parent window for non-modal windows.
  331. if (parent && !is_modal()) {
  332. SetParentWindow(parent);
  333. }
  334. if (transparent()) {
  335. // Setting the background color to clear will also hide the shadow.
  336. [window_ setBackgroundColor:[NSColor clearColor]];
  337. }
  338. if (windowType == "desktop") {
  339. [window_ setLevel:kCGDesktopWindowLevel - 1];
  340. [window_ setDisableKeyOrMainWindow:YES];
  341. [window_ setCollectionBehavior:(NSWindowCollectionBehaviorCanJoinAllSpaces |
  342. NSWindowCollectionBehaviorStationary |
  343. NSWindowCollectionBehaviorIgnoresCycle)];
  344. }
  345. bool focusable;
  346. if (options.Get(options::kFocusable, &focusable) && !focusable)
  347. [window_ setDisableKeyOrMainWindow:YES];
  348. if (transparent() || !has_frame()) {
  349. // Don't show title bar.
  350. [window_ setTitlebarAppearsTransparent:YES];
  351. [window_ setTitleVisibility:NSWindowTitleHidden];
  352. // Remove non-transparent corners, see http://git.io/vfonD.
  353. [window_ setOpaque:NO];
  354. }
  355. // Create a tab only if tabbing identifier is specified and window has
  356. // a native title bar.
  357. if (tabbingIdentifier.empty() || transparent() || !has_frame()) {
  358. if (@available(macOS 10.12, *)) {
  359. [window_ setTabbingMode:NSWindowTabbingModeDisallowed];
  360. }
  361. } else {
  362. if (@available(macOS 10.12, *)) {
  363. [window_ setTabbingIdentifier:base::SysUTF8ToNSString(tabbingIdentifier)];
  364. }
  365. }
  366. // Hide the title bar background
  367. if (title_bar_style_ != TitleBarStyle::NORMAL) {
  368. [window_ setTitlebarAppearsTransparent:YES];
  369. }
  370. // Hide the title bar.
  371. if (title_bar_style_ == TitleBarStyle::HIDDEN_INSET) {
  372. base::scoped_nsobject<NSToolbar> toolbar(
  373. [[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
  374. [toolbar setShowsBaselineSeparator:NO];
  375. [window_ setToolbar:toolbar];
  376. }
  377. // Resize to content bounds.
  378. bool use_content_size = false;
  379. options.Get(options::kUseContentSize, &use_content_size);
  380. if (!has_frame() || use_content_size)
  381. SetContentSize(gfx::Size(width, height));
  382. // Enable the NSView to accept first mouse event.
  383. bool acceptsFirstMouse = false;
  384. options.Get(options::kAcceptFirstMouse, &acceptsFirstMouse);
  385. [window_ setAcceptsFirstMouse:acceptsFirstMouse];
  386. // Disable auto-hiding cursor.
  387. bool disableAutoHideCursor = false;
  388. options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
  389. [window_ setDisableAutoHideCursor:disableAutoHideCursor];
  390. // Use an NSEvent monitor to listen for the wheel event.
  391. BOOL __block began = NO;
  392. wheel_event_monitor_ = [NSEvent
  393. addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
  394. handler:^(NSEvent* event) {
  395. if ([[event window] windowNumber] !=
  396. [window_ windowNumber])
  397. return event;
  398. if (!began && (([event phase] ==
  399. NSEventPhaseMayBegin) ||
  400. ([event phase] ==
  401. NSEventPhaseBegan))) {
  402. this->NotifyWindowScrollTouchBegin();
  403. began = YES;
  404. } else if (began &&
  405. (([event phase] ==
  406. NSEventPhaseEnded) ||
  407. ([event phase] ==
  408. NSEventPhaseCancelled))) {
  409. this->NotifyWindowScrollTouchEnd();
  410. began = NO;
  411. }
  412. return event;
  413. }];
  414. // Set maximizable state last to ensure zoom button does not get reset
  415. // by calls to other APIs.
  416. SetMaximizable(maximizable);
  417. // Default content view.
  418. SetContentView(new views::View());
  419. AddContentViewLayers(minimizable, closable);
  420. original_frame_ = [window_ frame];
  421. original_level_ = [window_ level];
  422. }
  423. NativeWindowMac::~NativeWindowMac() {
  424. if (wheel_event_monitor_)
  425. [NSEvent removeMonitor:wheel_event_monitor_];
  426. }
  427. void NativeWindowMac::SetContentView(views::View* view) {
  428. views::View* root_view = GetContentsView();
  429. if (content_view())
  430. root_view->RemoveChildView(content_view());
  431. set_content_view(view);
  432. root_view->AddChildView(content_view());
  433. if (buttons_view_) {
  434. // Ensure the buttons view are always floated on the top.
  435. [buttons_view_ removeFromSuperview];
  436. [[window_ contentView] addSubview:buttons_view_];
  437. }
  438. root_view->Layout();
  439. }
  440. void NativeWindowMac::Close() {
  441. // When this is a sheet showing, performClose won't work.
  442. if (is_modal() && parent() && IsVisible()) {
  443. NSWindow* window = parent()->GetNativeWindow().GetNativeNSWindow();
  444. if (NSWindow* sheetParent = [window sheetParent]) {
  445. base::ThreadTaskRunnerHandle::Get()->PostTask(
  446. FROM_HERE, base::BindOnce(base::RetainBlock(^{
  447. [sheetParent endSheet:window];
  448. })));
  449. }
  450. // Manually emit close event (not triggered from close fn)
  451. NotifyWindowCloseButtonClicked();
  452. CloseImmediately();
  453. return;
  454. }
  455. if (!IsClosable()) {
  456. WindowList::WindowCloseCancelled(this);
  457. return;
  458. }
  459. [window_ performClose:nil];
  460. }
  461. void NativeWindowMac::CloseImmediately() {
  462. // Remove event monitor before destroying window, otherwise the monitor may
  463. // call its callback after window has been destroyed.
  464. if (wheel_event_monitor_) {
  465. [NSEvent removeMonitor:wheel_event_monitor_];
  466. wheel_event_monitor_ = nil;
  467. }
  468. // Retain the child window before closing it. If the last reference to the
  469. // NSWindow goes away inside -[NSWindow close], then bad stuff can happen.
  470. // See e.g. http://crbug.com/616701.
  471. base::scoped_nsobject<NSWindow> child_window(window_,
  472. base::scoped_policy::RETAIN);
  473. [window_ close];
  474. }
  475. void NativeWindowMac::Focus(bool focus) {
  476. if (!IsVisible())
  477. return;
  478. if (focus) {
  479. [[NSApplication sharedApplication] activateIgnoringOtherApps:NO];
  480. [window_ makeKeyAndOrderFront:nil];
  481. } else {
  482. [window_ orderBack:nil];
  483. }
  484. }
  485. bool NativeWindowMac::IsFocused() {
  486. return [window_ isKeyWindow];
  487. }
  488. void NativeWindowMac::Show() {
  489. if (is_modal() && parent()) {
  490. if ([window_ sheetParent] == nil)
  491. [parent()->GetNativeWindow().GetNativeNSWindow()
  492. beginSheet:window_
  493. completionHandler:^(NSModalResponse){
  494. }];
  495. return;
  496. }
  497. // Reattach the window to the parent to actually show it.
  498. if (parent())
  499. InternalSetParentWindow(parent(), true);
  500. // This method is supposed to put focus on window, however if the app does not
  501. // have focus then "makeKeyAndOrderFront" will only show the window.
  502. [NSApp activateIgnoringOtherApps:YES];
  503. [window_ makeKeyAndOrderFront:nil];
  504. }
  505. void NativeWindowMac::ShowInactive() {
  506. // Reattach the window to the parent to actually show it.
  507. if (parent())
  508. InternalSetParentWindow(parent(), true);
  509. [window_ orderFrontRegardless];
  510. }
  511. void NativeWindowMac::Hide() {
  512. if (is_modal() && parent()) {
  513. [window_ orderOut:nil];
  514. [parent()->GetNativeWindow().GetNativeNSWindow() endSheet:window_];
  515. return;
  516. }
  517. // Deattach the window from the parent before.
  518. if (parent())
  519. InternalSetParentWindow(parent(), false);
  520. [window_ orderOut:nil];
  521. }
  522. bool NativeWindowMac::IsVisible() {
  523. bool occluded = [window_ occlusionState] == NSWindowOcclusionStateVisible;
  524. // For a window to be visible, it must be visible to the user in the
  525. // foreground of the app, which means that it should not be minimized or
  526. // occluded
  527. return [window_ isVisible] && !occluded && !IsMinimized();
  528. }
  529. bool NativeWindowMac::IsEnabled() {
  530. return [window_ attachedSheet] == nil;
  531. }
  532. void NativeWindowMac::SetEnabled(bool enable) {
  533. if (enable) {
  534. [window_ endSheet:[window_ attachedSheet]];
  535. } else {
  536. [window_ beginSheet:window_
  537. completionHandler:^(NSModalResponse returnCode) {
  538. NSLog(@"modal enabled");
  539. return;
  540. }];
  541. }
  542. }
  543. void NativeWindowMac::Maximize() {
  544. if (IsMaximized())
  545. return;
  546. // Take note of the current window size
  547. if (IsNormal())
  548. original_frame_ = [window_ frame];
  549. [window_ zoom:nil];
  550. }
  551. void NativeWindowMac::Unmaximize() {
  552. if (!IsMaximized())
  553. return;
  554. [window_ zoom:nil];
  555. }
  556. bool NativeWindowMac::IsMaximized() {
  557. if (([window_ styleMask] & NSWindowStyleMaskResizable) != 0) {
  558. return [window_ isZoomed];
  559. } else {
  560. NSRect rectScreen = [[NSScreen mainScreen] visibleFrame];
  561. NSRect rectWindow = [window_ frame];
  562. return (rectScreen.origin.x == rectWindow.origin.x &&
  563. rectScreen.origin.y == rectWindow.origin.y &&
  564. rectScreen.size.width == rectWindow.size.width &&
  565. rectScreen.size.height == rectWindow.size.height);
  566. }
  567. }
  568. void NativeWindowMac::Minimize() {
  569. if (IsMinimized())
  570. return;
  571. // Take note of the current window size
  572. if (IsNormal())
  573. original_frame_ = [window_ frame];
  574. [window_ miniaturize:nil];
  575. }
  576. void NativeWindowMac::Restore() {
  577. [window_ deminiaturize:nil];
  578. }
  579. bool NativeWindowMac::IsMinimized() {
  580. return [window_ isMiniaturized];
  581. }
  582. void NativeWindowMac::SetFullScreen(bool fullscreen) {
  583. if (fullscreen == IsFullscreen())
  584. return;
  585. // Take note of the current window size
  586. if (IsNormal())
  587. original_frame_ = [window_ frame];
  588. [window_ toggleFullScreenMode:nil];
  589. }
  590. bool NativeWindowMac::IsFullscreen() const {
  591. return [window_ styleMask] & NSWindowStyleMaskFullScreen;
  592. }
  593. void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
  594. // Do nothing if in fullscreen mode.
  595. if (IsFullscreen())
  596. return;
  597. // Check size constraints since setFrame does not check it.
  598. gfx::Size size = bounds.size();
  599. size.SetToMax(GetMinimumSize());
  600. gfx::Size max_size = GetMaximumSize();
  601. if (!max_size.IsEmpty())
  602. size.SetToMin(max_size);
  603. NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0, size.width(), size.height());
  604. // Flip coordinates based on the primary screen.
  605. NSScreen* screen = [[NSScreen screens] firstObject];
  606. cocoa_bounds.origin.y = NSHeight([screen frame]) - size.height() - bounds.y();
  607. [window_ setFrame:cocoa_bounds display:YES animate:animate];
  608. }
  609. gfx::Rect NativeWindowMac::GetBounds() {
  610. NSRect frame = [window_ frame];
  611. gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
  612. NSScreen* screen = [[NSScreen screens] firstObject];
  613. bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
  614. return bounds;
  615. }
  616. bool NativeWindowMac::IsNormal() {
  617. return NativeWindow::IsNormal() && !IsSimpleFullScreen();
  618. }
  619. gfx::Rect NativeWindowMac::GetNormalBounds() {
  620. if (IsNormal()) {
  621. return GetBounds();
  622. }
  623. NSRect frame = original_frame_;
  624. gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
  625. NSScreen* screen = [[NSScreen screens] firstObject];
  626. bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
  627. return bounds;
  628. // Works on OS_WIN !
  629. // return widget()->GetRestoredBounds();
  630. }
  631. void NativeWindowMac::SetContentSizeConstraints(
  632. const extensions::SizeConstraints& size_constraints) {
  633. auto convertSize = [this](const gfx::Size& size) {
  634. // Our frameless window still has titlebar attached, so setting contentSize
  635. // will result in actual content size being larger.
  636. if (!has_frame()) {
  637. NSRect frame = NSMakeRect(0, 0, size.width(), size.height());
  638. NSRect content = [window_ originalContentRectForFrameRect:frame];
  639. return content.size;
  640. } else {
  641. return NSMakeSize(size.width(), size.height());
  642. }
  643. };
  644. NSView* content = [window_ contentView];
  645. if (size_constraints.HasMinimumSize()) {
  646. NSSize min_size = convertSize(size_constraints.GetMinimumSize());
  647. [window_ setContentMinSize:[content convertSize:min_size toView:nil]];
  648. }
  649. if (size_constraints.HasMaximumSize()) {
  650. NSSize max_size = convertSize(size_constraints.GetMaximumSize());
  651. [window_ setContentMaxSize:[content convertSize:max_size toView:nil]];
  652. }
  653. NativeWindow::SetContentSizeConstraints(size_constraints);
  654. }
  655. void NativeWindowMac::MoveTop() {
  656. [window_ orderWindow:NSWindowAbove relativeTo:0];
  657. }
  658. void NativeWindowMac::SetResizable(bool resizable) {
  659. SetStyleMask(resizable, NSWindowStyleMaskResizable);
  660. }
  661. bool NativeWindowMac::IsResizable() {
  662. return [window_ styleMask] & NSWindowStyleMaskResizable;
  663. }
  664. void NativeWindowMac::SetAspectRatio(double aspect_ratio,
  665. const gfx::Size& extra_size) {
  666. NativeWindow::SetAspectRatio(aspect_ratio, extra_size);
  667. // Reset the behaviour to default if aspect_ratio is set to 0 or less.
  668. if (aspect_ratio > 0.0)
  669. [window_ setAspectRatio:NSMakeSize(aspect_ratio, 1.0)];
  670. else
  671. [window_ setResizeIncrements:NSMakeSize(1.0, 1.0)];
  672. }
  673. void NativeWindowMac::PreviewFile(const std::string& path,
  674. const std::string& display_name) {
  675. preview_item_.reset([[AtomPreviewItem alloc]
  676. initWithURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(path)]
  677. title:base::SysUTF8ToNSString(display_name)]);
  678. [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
  679. }
  680. void NativeWindowMac::CloseFilePreview() {
  681. if ([QLPreviewPanel sharedPreviewPanelExists]) {
  682. [[QLPreviewPanel sharedPreviewPanel] close];
  683. }
  684. }
  685. void NativeWindowMac::SetMovable(bool movable) {
  686. [window_ setMovable:movable];
  687. }
  688. bool NativeWindowMac::IsMovable() {
  689. return [window_ isMovable];
  690. }
  691. void NativeWindowMac::SetMinimizable(bool minimizable) {
  692. SetStyleMask(minimizable, NSMiniaturizableWindowMask);
  693. }
  694. bool NativeWindowMac::IsMinimizable() {
  695. return [window_ styleMask] & NSMiniaturizableWindowMask;
  696. }
  697. void NativeWindowMac::SetMaximizable(bool maximizable) {
  698. [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:maximizable];
  699. }
  700. bool NativeWindowMac::IsMaximizable() {
  701. return [[window_ standardWindowButton:NSWindowZoomButton] isEnabled];
  702. }
  703. void NativeWindowMac::SetFullScreenable(bool fullscreenable) {
  704. SetCollectionBehavior(fullscreenable,
  705. NSWindowCollectionBehaviorFullScreenPrimary);
  706. // On EL Capitan this flag is required to hide fullscreen button.
  707. SetCollectionBehavior(!fullscreenable,
  708. NSWindowCollectionBehaviorFullScreenAuxiliary);
  709. }
  710. bool NativeWindowMac::IsFullScreenable() {
  711. NSUInteger collectionBehavior = [window_ collectionBehavior];
  712. return collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary;
  713. }
  714. void NativeWindowMac::SetClosable(bool closable) {
  715. SetStyleMask(closable, NSWindowStyleMaskClosable);
  716. }
  717. bool NativeWindowMac::IsClosable() {
  718. return [window_ styleMask] & NSWindowStyleMaskClosable;
  719. }
  720. void NativeWindowMac::SetAlwaysOnTop(ui::ZOrderLevel z_order,
  721. const std::string& level,
  722. int relativeLevel,
  723. std::string* error) {
  724. int windowLevel = NSNormalWindowLevel;
  725. bool level_changed = z_order != widget()->GetZOrderLevel();
  726. CGWindowLevel maxWindowLevel = CGWindowLevelForKey(kCGMaximumWindowLevelKey);
  727. CGWindowLevel minWindowLevel = CGWindowLevelForKey(kCGMinimumWindowLevelKey);
  728. if (z_order != ui::ZOrderLevel::kNormal) {
  729. if (level == "floating") {
  730. windowLevel = NSFloatingWindowLevel;
  731. } else if (level == "torn-off-menu") {
  732. windowLevel = NSTornOffMenuWindowLevel;
  733. } else if (level == "modal-panel") {
  734. windowLevel = NSModalPanelWindowLevel;
  735. } else if (level == "main-menu") {
  736. windowLevel = NSMainMenuWindowLevel;
  737. } else if (level == "status") {
  738. windowLevel = NSStatusWindowLevel;
  739. } else if (level == "pop-up-menu") {
  740. windowLevel = NSPopUpMenuWindowLevel;
  741. } else if (level == "screen-saver") {
  742. windowLevel = NSScreenSaverWindowLevel;
  743. } else if (level == "dock") {
  744. // Deprecated by macOS, but kept for backwards compatibility
  745. windowLevel = NSDockWindowLevel;
  746. }
  747. }
  748. NSInteger newLevel = windowLevel + relativeLevel;
  749. if (newLevel >= minWindowLevel && newLevel <= maxWindowLevel) {
  750. was_maximizable_ = IsMaximizable();
  751. [window_ setLevel:newLevel];
  752. // Set level will make the zoom button revert to default, probably
  753. // a bug of Cocoa or macOS.
  754. [[window_ standardWindowButton:NSWindowZoomButton]
  755. setEnabled:was_maximizable_];
  756. } else {
  757. *error = std::string([[NSString
  758. stringWithFormat:@"relativeLevel must be between %d and %d",
  759. minWindowLevel, maxWindowLevel] UTF8String]);
  760. }
  761. // This must be notified at the very end or IsAlwaysOnTop
  762. // will not yet have been updated to reflect the new status
  763. if (level_changed)
  764. NativeWindow::NotifyWindowAlwaysOnTopChanged();
  765. }
  766. ui::ZOrderLevel NativeWindowMac::GetZOrderLevel() {
  767. return widget()->GetZOrderLevel();
  768. }
  769. void NativeWindowMac::Center() {
  770. [window_ center];
  771. }
  772. void NativeWindowMac::Invalidate() {
  773. [window_ flushWindow];
  774. [[window_ contentView] setNeedsDisplay:YES];
  775. }
  776. void NativeWindowMac::SetTitle(const std::string& title) {
  777. [window_ setTitle:base::SysUTF8ToNSString(title)];
  778. }
  779. std::string NativeWindowMac::GetTitle() {
  780. return base::SysNSStringToUTF8([window_ title]);
  781. }
  782. void NativeWindowMac::FlashFrame(bool flash) {
  783. if (flash) {
  784. attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest];
  785. } else {
  786. [NSApp cancelUserAttentionRequest:attention_request_id_];
  787. attention_request_id_ = 0;
  788. }
  789. }
  790. void NativeWindowMac::SetSkipTaskbar(bool skip) {}
  791. bool NativeWindowMac::IsExcludedFromShownWindowsMenu() {
  792. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  793. return [window isExcludedFromWindowsMenu];
  794. }
  795. void NativeWindowMac::SetExcludedFromShownWindowsMenu(bool excluded) {
  796. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  797. [window setExcludedFromWindowsMenu:excluded];
  798. }
  799. void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
  800. NSWindow* window = GetNativeWindow().GetNativeNSWindow();
  801. if (simple_fullscreen && !is_simple_fullscreen_) {
  802. is_simple_fullscreen_ = true;
  803. // Take note of the current window size and level
  804. if (IsNormal()) {
  805. original_frame_ = [window_ frame];
  806. original_level_ = [window_ level];
  807. }
  808. simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions];
  809. simple_fullscreen_mask_ = [window styleMask];
  810. // We can simulate the pre-Lion fullscreen by auto-hiding the dock and menu
  811. // bar
  812. NSApplicationPresentationOptions options =
  813. NSApplicationPresentationAutoHideDock |
  814. NSApplicationPresentationAutoHideMenuBar;
  815. [NSApp setPresentationOptions:options];
  816. was_maximizable_ = IsMaximizable();
  817. was_movable_ = IsMovable();
  818. NSRect fullscreenFrame = [window.screen frame];
  819. // If our app has dock hidden, set the window level higher so another app's
  820. // menu bar doesn't appear on top of our fullscreen app.
  821. if ([[NSRunningApplication currentApplication] activationPolicy] !=
  822. NSApplicationActivationPolicyRegular) {
  823. window.level = NSPopUpMenuWindowLevel;
  824. }
  825. if (!fullscreen_window_title()) {
  826. // Hide the titlebar
  827. SetStyleMask(false, NSWindowStyleMaskTitled);
  828. // Resize the window to accomodate the _entire_ screen size
  829. fullscreenFrame.size.height -=
  830. [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
  831. } else if (!window_button_visibility_.has_value()) {
  832. // Lets keep previous behaviour - hide window controls in titled
  833. // fullscreen mode when not specified otherwise.
  834. [[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
  835. [[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
  836. [[window standardWindowButton:NSWindowCloseButton] setHidden:YES];
  837. }
  838. [window setFrame:fullscreenFrame display:YES animate:YES];
  839. // Fullscreen windows can't be resized, minimized, maximized, or moved
  840. SetMinimizable(false);
  841. SetResizable(false);
  842. SetMaximizable(false);
  843. SetMovable(false);
  844. } else if (!simple_fullscreen && is_simple_fullscreen_) {
  845. is_simple_fullscreen_ = false;
  846. if (!fullscreen_window_title()) {
  847. // Restore the titlebar
  848. SetStyleMask(true, NSWindowStyleMaskTitled);
  849. }
  850. // Restore window controls visibility state
  851. const bool window_button_hidden = !window_button_visibility_.value_or(true);
  852. [[window standardWindowButton:NSWindowZoomButton]
  853. setHidden:window_button_hidden];
  854. [[window standardWindowButton:NSWindowMiniaturizeButton]
  855. setHidden:window_button_hidden];
  856. [[window standardWindowButton:NSWindowCloseButton]
  857. setHidden:window_button_hidden];
  858. [window setFrame:original_frame_ display:YES animate:YES];
  859. window.level = original_level_;
  860. [NSApp setPresentationOptions:simple_fullscreen_options_];
  861. // Restore original style mask
  862. ScopedDisableResize disable_resize;
  863. [window_ setStyleMask:simple_fullscreen_mask_];
  864. // Restore window manipulation abilities
  865. SetMaximizable(was_maximizable_);
  866. SetMovable(was_movable_);
  867. }
  868. }
  869. bool NativeWindowMac::IsSimpleFullScreen() {
  870. return is_simple_fullscreen_;
  871. }
  872. void NativeWindowMac::SetKiosk(bool kiosk) {
  873. if (kiosk && !is_kiosk_) {
  874. kiosk_options_ = [NSApp currentSystemPresentationOptions];
  875. NSApplicationPresentationOptions options =
  876. NSApplicationPresentationHideDock |
  877. NSApplicationPresentationHideMenuBar |
  878. NSApplicationPresentationDisableAppleMenu |
  879. NSApplicationPresentationDisableProcessSwitching |
  880. NSApplicationPresentationDisableForceQuit |
  881. NSApplicationPresentationDisableSessionTermination |
  882. NSApplicationPresentationDisableHideApplication;
  883. [NSApp setPresentationOptions:options];
  884. is_kiosk_ = true;
  885. was_fullscreen_ = IsFullscreen();
  886. if (!was_fullscreen_)
  887. SetFullScreen(true);
  888. } else if (!kiosk && is_kiosk_) {
  889. is_kiosk_ = false;
  890. if (!was_fullscreen_)
  891. SetFullScreen(false);
  892. [NSApp setPresentationOptions:kiosk_options_];
  893. }
  894. }
  895. bool NativeWindowMac::IsKiosk() {
  896. return is_kiosk_;
  897. }
  898. void NativeWindowMac::SetBackgroundColor(SkColor color) {
  899. base::ScopedCFTypeRef<CGColorRef> cgcolor(
  900. skia::CGColorCreateFromSkColor(color));
  901. [[[window_ contentView] layer] setBackgroundColor:cgcolor];
  902. }
  903. void NativeWindowMac::SetHasShadow(bool has_shadow) {
  904. [window_ setHasShadow:has_shadow];
  905. }
  906. bool NativeWindowMac::HasShadow() {
  907. return [window_ hasShadow];
  908. }
  909. void NativeWindowMac::SetOpacity(const double opacity) {
  910. const double boundedOpacity = base::ClampToRange(opacity, 0.0, 1.0);
  911. [window_ setAlphaValue:boundedOpacity];
  912. }
  913. double NativeWindowMac::GetOpacity() {
  914. return [window_ alphaValue];
  915. }
  916. void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
  917. [window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
  918. }
  919. std::string NativeWindowMac::GetRepresentedFilename() {
  920. return base::SysNSStringToUTF8([window_ representedFilename]);
  921. }
  922. void NativeWindowMac::SetDocumentEdited(bool edited) {
  923. [window_ setDocumentEdited:edited];
  924. }
  925. bool NativeWindowMac::IsDocumentEdited() {
  926. return [window_ isDocumentEdited];
  927. }
  928. void NativeWindowMac::SetIgnoreMouseEvents(bool ignore, bool forward) {
  929. [window_ setIgnoresMouseEvents:ignore];
  930. if (!ignore) {
  931. SetForwardMouseMessages(NO);
  932. } else {
  933. SetForwardMouseMessages(forward);
  934. }
  935. }
  936. void NativeWindowMac::SetContentProtection(bool enable) {
  937. [window_
  938. setSharingType:enable ? NSWindowSharingNone : NSWindowSharingReadOnly];
  939. }
  940. void NativeWindowMac::SetFocusable(bool focusable) {
  941. // No known way to unfocus the window if it had the focus. Here we do not
  942. // want to call Focus(false) because it moves the window to the back, i.e.
  943. // at the bottom in term of z-order.
  944. [window_ setDisableKeyOrMainWindow:!focusable];
  945. }
  946. void NativeWindowMac::AddBrowserView(NativeBrowserView* view) {
  947. [CATransaction begin];
  948. [CATransaction setDisableActions:YES];
  949. if (!view) {
  950. [CATransaction commit];
  951. return;
  952. }
  953. add_browser_view(view);
  954. auto* native_view =
  955. view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
  956. [[window_ contentView] addSubview:native_view
  957. positioned:NSWindowAbove
  958. relativeTo:nil];
  959. native_view.hidden = NO;
  960. [CATransaction commit];
  961. }
  962. void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
  963. [CATransaction begin];
  964. [CATransaction setDisableActions:YES];
  965. if (!view) {
  966. [CATransaction commit];
  967. return;
  968. }
  969. [view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
  970. removeFromSuperview];
  971. remove_browser_view(view);
  972. [CATransaction commit];
  973. }
  974. void NativeWindowMac::SetParentWindow(NativeWindow* parent) {
  975. InternalSetParentWindow(parent, IsVisible());
  976. }
  977. gfx::NativeView NativeWindowMac::GetNativeView() const {
  978. return [window_ contentView];
  979. }
  980. gfx::NativeWindow NativeWindowMac::GetNativeWindow() const {
  981. return window_;
  982. }
  983. gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() const {
  984. return gfx::kNullAcceleratedWidget;
  985. }
  986. NativeWindowHandle NativeWindowMac::GetNativeWindowHandle() const {
  987. return [window_ contentView];
  988. }
  989. void NativeWindowMac::SetProgressBar(double progress,
  990. const NativeWindow::ProgressState state) {
  991. NSDockTile* dock_tile = [NSApp dockTile];
  992. // Sometimes macOS would install a default contentView for dock, we must
  993. // verify whether NSProgressIndicator has been installed.
  994. bool first_time = !dock_tile.contentView ||
  995. [[dock_tile.contentView subviews] count] == 0 ||
  996. ![[[dock_tile.contentView subviews] lastObject]
  997. isKindOfClass:[NSProgressIndicator class]];
  998. // For the first time API invoked, we need to create a ContentView in
  999. // DockTile.
  1000. if (first_time) {
  1001. NSImageView* image_view = [[[NSImageView alloc] init] autorelease];
  1002. [image_view setImage:[NSApp applicationIconImage]];
  1003. [dock_tile setContentView:image_view];
  1004. NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0);
  1005. NSProgressIndicator* progress_indicator =
  1006. [[[AtomProgressBar alloc] initWithFrame:frame] autorelease];
  1007. [progress_indicator setStyle:NSProgressIndicatorBarStyle];
  1008. [progress_indicator setIndeterminate:NO];
  1009. [progress_indicator setBezeled:YES];
  1010. [progress_indicator setMinValue:0];
  1011. [progress_indicator setMaxValue:1];
  1012. [progress_indicator setHidden:NO];
  1013. [dock_tile.contentView addSubview:progress_indicator];
  1014. }
  1015. NSProgressIndicator* progress_indicator = static_cast<NSProgressIndicator*>(
  1016. [[[dock_tile contentView] subviews] lastObject]);
  1017. if (progress < 0) {
  1018. [progress_indicator setHidden:YES];
  1019. } else if (progress > 1) {
  1020. [progress_indicator setHidden:NO];
  1021. [progress_indicator setIndeterminate:YES];
  1022. [progress_indicator setDoubleValue:1];
  1023. } else {
  1024. [progress_indicator setHidden:NO];
  1025. [progress_indicator setDoubleValue:progress];
  1026. }
  1027. [dock_tile display];
  1028. }
  1029. void NativeWindowMac::SetOverlayIcon(const gfx::Image& overlay,
  1030. const std::string& description) {}
  1031. void NativeWindowMac::SetVisibleOnAllWorkspaces(bool visible,
  1032. bool visibleOnFullScreen) {
  1033. SetCollectionBehavior(visible, NSWindowCollectionBehaviorCanJoinAllSpaces);
  1034. SetCollectionBehavior(visibleOnFullScreen,
  1035. NSWindowCollectionBehaviorFullScreenAuxiliary);
  1036. }
  1037. bool NativeWindowMac::IsVisibleOnAllWorkspaces() {
  1038. NSUInteger collectionBehavior = [window_ collectionBehavior];
  1039. return collectionBehavior & NSWindowCollectionBehaviorCanJoinAllSpaces;
  1040. }
  1041. void NativeWindowMac::SetAutoHideCursor(bool auto_hide) {
  1042. [window_ setDisableAutoHideCursor:!auto_hide];
  1043. }
  1044. void NativeWindowMac::SelectPreviousTab() {
  1045. if (@available(macOS 10.12, *)) {
  1046. [window_ selectPreviousTab:nil];
  1047. }
  1048. }
  1049. void NativeWindowMac::SelectNextTab() {
  1050. if (@available(macOS 10.12, *)) {
  1051. [window_ selectNextTab:nil];
  1052. }
  1053. }
  1054. void NativeWindowMac::MergeAllWindows() {
  1055. if (@available(macOS 10.12, *)) {
  1056. [window_ mergeAllWindows:nil];
  1057. }
  1058. }
  1059. void NativeWindowMac::MoveTabToNewWindow() {
  1060. if (@available(macOS 10.12, *)) {
  1061. [window_ moveTabToNewWindow:nil];
  1062. }
  1063. }
  1064. void NativeWindowMac::ToggleTabBar() {
  1065. if (@available(macOS 10.12, *)) {
  1066. [window_ toggleTabBar:nil];
  1067. }
  1068. }
  1069. bool NativeWindowMac::AddTabbedWindow(NativeWindow* window) {
  1070. if (window_ == window->GetNativeWindow().GetNativeNSWindow()) {
  1071. return false;
  1072. } else {
  1073. if (@available(macOS 10.12, *))
  1074. [window_ addTabbedWindow:window->GetNativeWindow().GetNativeNSWindow()
  1075. ordered:NSWindowAbove];
  1076. }
  1077. return true;
  1078. }
  1079. bool NativeWindowMac::SetWindowButtonVisibility(bool visible) {
  1080. if (title_bar_style_ == TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER) {
  1081. return false;
  1082. }
  1083. window_button_visibility_ = visible;
  1084. [[window_ standardWindowButton:NSWindowCloseButton] setHidden:!visible];
  1085. [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
  1086. [[window_ standardWindowButton:NSWindowZoomButton] setHidden:!visible];
  1087. return true;
  1088. }
  1089. void NativeWindowMac::SetVibrancy(const std::string& type) {
  1090. NSView* vibrant_view = [window_ vibrantView];
  1091. if (type.empty()) {
  1092. if (background_color_before_vibrancy_) {
  1093. [window_ setBackgroundColor:background_color_before_vibrancy_];
  1094. [window_ setTitlebarAppearsTransparent:transparency_before_vibrancy_];
  1095. }
  1096. if (vibrant_view == nil)
  1097. return;
  1098. [vibrant_view removeFromSuperview];
  1099. [window_ setVibrantView:nil];
  1100. return;
  1101. }
  1102. background_color_before_vibrancy_.reset([[window_ backgroundColor] retain]);
  1103. transparency_before_vibrancy_ = [window_ titlebarAppearsTransparent];
  1104. if (title_bar_style_ != TitleBarStyle::NORMAL) {
  1105. [window_ setTitlebarAppearsTransparent:YES];
  1106. [window_ setBackgroundColor:[NSColor clearColor]];
  1107. }
  1108. NSVisualEffectView* effect_view = (NSVisualEffectView*)vibrant_view;
  1109. if (effect_view == nil) {
  1110. effect_view = [[[NSVisualEffectView alloc]
  1111. initWithFrame:[[window_ contentView] bounds]] autorelease];
  1112. [window_ setVibrantView:(NSView*)effect_view];
  1113. [effect_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1114. [effect_view setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
  1115. [effect_view setState:NSVisualEffectStateActive];
  1116. [[window_ contentView] addSubview:effect_view
  1117. positioned:NSWindowBelow
  1118. relativeTo:nil];
  1119. }
  1120. std::string dep_warn =
  1121. " has been deprecated and will be removed in a future version of macOS.";
  1122. node::Environment* env =
  1123. node::Environment::GetCurrent(v8::Isolate::GetCurrent());
  1124. NSVisualEffectMaterial vibrancyType;
  1125. if (type == "appearance-based") {
  1126. EmitDeprecationWarning(
  1127. env, "NSVisualEffectMaterialAppearanceBased" + dep_warn, "electron");
  1128. vibrancyType = NSVisualEffectMaterialAppearanceBased;
  1129. } else if (type == "light") {
  1130. EmitDeprecationWarning(env, "NSVisualEffectMaterialLight" + dep_warn,
  1131. "electron");
  1132. vibrancyType = NSVisualEffectMaterialLight;
  1133. } else if (type == "dark") {
  1134. EmitDeprecationWarning(env, "NSVisualEffectMaterialDark" + dep_warn,
  1135. "electron");
  1136. vibrancyType = NSVisualEffectMaterialDark;
  1137. } else if (type == "titlebar") {
  1138. vibrancyType = NSVisualEffectMaterialTitlebar;
  1139. }
  1140. if (@available(macOS 10.11, *)) {
  1141. // TODO(codebytere): Use NSVisualEffectMaterial* constants directly once
  1142. // they are available in the minimum SDK version
  1143. if (type == "selection") {
  1144. // NSVisualEffectMaterialSelection
  1145. vibrancyType = static_cast<NSVisualEffectMaterial>(4);
  1146. } else if (type == "menu") {
  1147. // NSVisualEffectMaterialMenu
  1148. vibrancyType = static_cast<NSVisualEffectMaterial>(5);
  1149. } else if (type == "popover") {
  1150. // NSVisualEffectMaterialPopover
  1151. vibrancyType = static_cast<NSVisualEffectMaterial>(6);
  1152. } else if (type == "sidebar") {
  1153. // NSVisualEffectMaterialSidebar
  1154. vibrancyType = static_cast<NSVisualEffectMaterial>(7);
  1155. } else if (type == "medium-light") {
  1156. // NSVisualEffectMaterialMediumLight
  1157. EmitDeprecationWarning(
  1158. env, "NSVisualEffectMaterialMediumLight" + dep_warn, "electron");
  1159. vibrancyType = static_cast<NSVisualEffectMaterial>(8);
  1160. } else if (type == "ultra-dark") {
  1161. // NSVisualEffectMaterialUltraDark
  1162. EmitDeprecationWarning(env, "NSVisualEffectMaterialUltraDark" + dep_warn,
  1163. "electron");
  1164. vibrancyType = static_cast<NSVisualEffectMaterial>(9);
  1165. }
  1166. }
  1167. if (@available(macOS 10.14, *)) {
  1168. if (type == "header") {
  1169. // NSVisualEffectMaterialHeaderView
  1170. vibrancyType = static_cast<NSVisualEffectMaterial>(10);
  1171. } else if (type == "sheet") {
  1172. // NSVisualEffectMaterialSheet
  1173. vibrancyType = static_cast<NSVisualEffectMaterial>(11);
  1174. } else if (type == "window") {
  1175. // NSVisualEffectMaterialWindowBackground
  1176. vibrancyType = static_cast<NSVisualEffectMaterial>(12);
  1177. } else if (type == "hud") {
  1178. // NSVisualEffectMaterialHUDWindow
  1179. vibrancyType = static_cast<NSVisualEffectMaterial>(13);
  1180. } else if (type == "fullscreen-ui") {
  1181. // NSVisualEffectMaterialFullScreenUI
  1182. vibrancyType = static_cast<NSVisualEffectMaterial>(16);
  1183. } else if (type == "tooltip") {
  1184. // NSVisualEffectMaterialToolTip
  1185. vibrancyType = static_cast<NSVisualEffectMaterial>(17);
  1186. } else if (type == "content") {
  1187. // NSVisualEffectMaterialContentBackground
  1188. vibrancyType = static_cast<NSVisualEffectMaterial>(18);
  1189. } else if (type == "under-window") {
  1190. // NSVisualEffectMaterialUnderWindowBackground
  1191. vibrancyType = static_cast<NSVisualEffectMaterial>(21);
  1192. } else if (type == "under-page") {
  1193. // NSVisualEffectMaterialUnderPageBackground
  1194. vibrancyType = static_cast<NSVisualEffectMaterial>(22);
  1195. }
  1196. }
  1197. if (vibrancyType)
  1198. [effect_view setMaterial:vibrancyType];
  1199. }
  1200. void NativeWindowMac::SetTouchBar(
  1201. const std::vector<mate::PersistentDictionary>& items) {
  1202. if (@available(macOS 10.12.2, *)) {
  1203. touch_bar_.reset([[AtomTouchBar alloc]
  1204. initWithDelegate:window_delegate_.get()
  1205. window:this
  1206. settings:items]);
  1207. [window_ setTouchBar:nil];
  1208. }
  1209. }
  1210. void NativeWindowMac::RefreshTouchBarItem(const std::string& item_id) {
  1211. if (@available(macOS 10.12.2, *)) {
  1212. if (touch_bar_ && [window_ touchBar])
  1213. [touch_bar_ refreshTouchBarItem:[window_ touchBar] id:item_id];
  1214. }
  1215. }
  1216. void NativeWindowMac::SetEscapeTouchBarItem(
  1217. const mate::PersistentDictionary& item) {
  1218. if (@available(macOS 10.12.2, *)) {
  1219. if (touch_bar_ && [window_ touchBar])
  1220. [touch_bar_ setEscapeTouchBarItem:item forTouchBar:[window_ touchBar]];
  1221. }
  1222. }
  1223. gfx::Rect NativeWindowMac::ContentBoundsToWindowBounds(
  1224. const gfx::Rect& bounds) const {
  1225. if (has_frame()) {
  1226. gfx::Rect window_bounds(
  1227. [window_ frameRectForContentRect:bounds.ToCGRect()]);
  1228. int frame_height = window_bounds.height() - bounds.height();
  1229. window_bounds.set_y(window_bounds.y() - frame_height);
  1230. return window_bounds;
  1231. } else {
  1232. return bounds;
  1233. }
  1234. }
  1235. gfx::Rect NativeWindowMac::WindowBoundsToContentBounds(
  1236. const gfx::Rect& bounds) const {
  1237. if (has_frame()) {
  1238. gfx::Rect content_bounds(
  1239. [window_ contentRectForFrameRect:bounds.ToCGRect()]);
  1240. int frame_height = bounds.height() - content_bounds.height();
  1241. content_bounds.set_y(content_bounds.y() + frame_height);
  1242. return content_bounds;
  1243. } else {
  1244. return bounds;
  1245. }
  1246. }
  1247. bool NativeWindowMac::CanResize() const {
  1248. return resizable_;
  1249. }
  1250. views::View* NativeWindowMac::GetContentsView() {
  1251. return root_view_.get();
  1252. }
  1253. void NativeWindowMac::AddContentViewLayers(bool minimizable, bool closable) {
  1254. // Make sure the bottom corner is rounded for non-modal windows:
  1255. // http://crbug.com/396264.
  1256. if (!is_modal()) {
  1257. // For normal window, we need to explicitly set layer for contentView to
  1258. // make setBackgroundColor work correctly.
  1259. // There is no need to do so for frameless window, and doing so would make
  1260. // titleBarStyle stop working.
  1261. if (has_frame()) {
  1262. base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]);
  1263. [background_layer
  1264. setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
  1265. [[window_ contentView] setLayer:background_layer];
  1266. }
  1267. [[window_ contentView] setWantsLayer:YES];
  1268. }
  1269. if (!has_frame()) {
  1270. // In OSX 10.10, adding subviews to the root view for the NSView hierarchy
  1271. // produces warnings. To eliminate the warnings, we resize the contentView
  1272. // to fill the window, and add subviews to that.
  1273. // http://crbug.com/380412
  1274. if (!original_set_frame_size) {
  1275. Class cl = [[window_ contentView] class];
  1276. original_set_frame_size = class_replaceMethod(
  1277. cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}");
  1278. original_view_did_move_to_superview =
  1279. class_replaceMethod(cl, @selector(viewDidMoveToSuperview),
  1280. (IMP)ViewDidMoveToSuperview, "v@:");
  1281. [[window_ contentView] viewDidMoveToWindow];
  1282. }
  1283. // The fullscreen button should always be hidden for frameless window.
  1284. [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES];
  1285. if (title_bar_style_ == TitleBarStyle::CUSTOM_BUTTONS_ON_HOVER) {
  1286. buttons_view_.reset(
  1287. [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]);
  1288. // NSWindowStyleMaskFullSizeContentView does not work with zoom button
  1289. SetFullScreenable(false);
  1290. if (!minimizable)
  1291. [[buttons_view_ viewWithTag:2] removeFromSuperview];
  1292. if (!closable)
  1293. [[buttons_view_ viewWithTag:1] removeFromSuperview];
  1294. [[window_ contentView] addSubview:buttons_view_];
  1295. } else {
  1296. if (title_bar_style_ != TitleBarStyle::NORMAL)
  1297. return;
  1298. // Hide the window buttons.
  1299. [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
  1300. [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
  1301. [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES];
  1302. }
  1303. // Some third-party macOS utilities check the zoom button's enabled state to
  1304. // determine whether to show custom UI on hover, so we disable it here to
  1305. // prevent them from doing so in a frameless app window.
  1306. [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO];
  1307. }
  1308. }
  1309. void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent,
  1310. bool attach) {
  1311. if (is_modal())
  1312. return;
  1313. NativeWindow::SetParentWindow(parent);
  1314. // Do not remove/add if we are already properly attached.
  1315. if (attach && parent &&
  1316. [window_ parentWindow] == parent->GetNativeWindow().GetNativeNSWindow())
  1317. return;
  1318. // Remove current parent window.
  1319. if ([window_ parentWindow])
  1320. [[window_ parentWindow] removeChildWindow:window_];
  1321. // Set new parent window.
  1322. // Note that this method will force the window to become visible.
  1323. if (parent && attach)
  1324. [parent->GetNativeWindow().GetNativeNSWindow()
  1325. addChildWindow:window_
  1326. ordered:NSWindowAbove];
  1327. }
  1328. void NativeWindowMac::SetForwardMouseMessages(bool forward) {
  1329. [window_ setAcceptsMouseMovedEvents:forward];
  1330. }
  1331. void NativeWindowMac::OverrideNSWindowContentView() {
  1332. // When using `views::Widget` to hold WebContents, Chromium would use
  1333. // `BridgedContentView` as content view, which does not support draggable
  1334. // regions. In order to make draggable regions work, we have to replace the
  1335. // content view with a simple NSView.
  1336. if (has_frame()) {
  1337. container_view_.reset(
  1338. [[ElectronAdapatedContentView alloc] initWithShell:this]);
  1339. } else {
  1340. container_view_.reset([[FullSizeContentView alloc] init]);
  1341. [container_view_ setFrame:[[[window_ contentView] superview] bounds]];
  1342. }
  1343. [container_view_
  1344. setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
  1345. [window_ setContentView:container_view_];
  1346. AddContentViewLayers(IsMinimizable(), IsClosable());
  1347. }
  1348. void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
  1349. // Changing the styleMask of a frameless windows causes it to change size so
  1350. // we explicitly disable resizing while setting it.
  1351. ScopedDisableResize disable_resize;
  1352. bool was_maximizable = IsMaximizable();
  1353. if (on)
  1354. [window_ setStyleMask:[window_ styleMask] | flag];
  1355. else
  1356. [window_ setStyleMask:[window_ styleMask] & (~flag)];
  1357. // Change style mask will make the zoom button revert to default, probably
  1358. // a bug of Cocoa or macOS.
  1359. SetMaximizable(was_maximizable);
  1360. }
  1361. void NativeWindowMac::SetCollectionBehavior(bool on, NSUInteger flag) {
  1362. bool was_maximizable = IsMaximizable();
  1363. if (on)
  1364. [window_ setCollectionBehavior:[window_ collectionBehavior] | flag];
  1365. else
  1366. [window_ setCollectionBehavior:[window_ collectionBehavior] & (~flag)];
  1367. // Change collectionBehavior will make the zoom button revert to default,
  1368. // probably a bug of Cocoa or macOS.
  1369. SetMaximizable(was_maximizable);
  1370. }
  1371. // static
  1372. NativeWindow* NativeWindow::Create(const mate::Dictionary& options,
  1373. NativeWindow* parent) {
  1374. return new NativeWindowMac(options, parent);
  1375. }
  1376. } // namespace electron