/
immersive_mode_controller.mm
618 lines (530 loc) · 23 KB
/
immersive_mode_controller.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/remote_cocoa/app_shim/immersive_mode_controller.h"
#include "base/auto_reset.h"
#include "base/check.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_block.h"
#import "components/remote_cocoa/app_shim/immersive_mode_delegate_mac.h"
#import "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
#import "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
#include "ui/gfx/geometry/rect.h"
namespace {
const double kThinControllerHeight = 0.5;
// TODO(https://crbug.com/1373552): use constraints / autoresizingmask instead
// of manually setting the frame size.
void PropagateFrameSizeToViewsSubviews(NSView* view) {
for (NSView* sub_view in view.subviews) {
if ([sub_view isKindOfClass:[BridgedContentView class]]) {
[sub_view setFrameSize:view.frame.size];
}
}
}
NSView* GetNSTitlebarContainerViewFromWindow(NSWindow* window) {
for (NSView* view in window.contentView.subviews) {
if ([view isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
return view;
}
}
return nil;
}
} // namespace
@interface ImmersiveModeTitlebarObserver () {
base::WeakPtr<remote_cocoa::ImmersiveModeController> _controller;
NSView* _titlebarContainerView;
}
@end
@implementation ImmersiveModeTitlebarObserver
- (instancetype)initWithController:
(base::WeakPtr<remote_cocoa::ImmersiveModeController>)
controller
titlebarContainerView:(NSView*)titlebarContainerView {
self = [super init];
if (self) {
_controller = std::move(controller);
_titlebarContainerView = titlebarContainerView;
[_titlebarContainerView addObserver:self
forKeyPath:@"frame"
options:NSKeyValueObservingOptionInitial |
NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void)dealloc {
[_titlebarContainerView removeObserver:self forKeyPath:@"frame"];
[super dealloc];
}
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id>*)change
context:(void*)context {
if (![keyPath isEqualToString:@"frame"]) {
return;
}
NSRect frame = [change[@"new"] rectValue];
_controller->OnTitlebarFrameDidChange(frame);
}
@end
// A stub NSWindowDelegate class that will be used to map the AppKit controlled
// NSWindow to the overlay view widget's NSWindow. The delegate will be used to
// help with input routing.
@interface ImmersiveModeMapper : NSObject <ImmersiveModeDelegate>
@property(assign) NSWindow* originalHostingWindow;
@end
@implementation ImmersiveModeMapper
@synthesize originalHostingWindow = _originalHostingWindow;
@end
// Host of the overlay view.
@interface ImmersiveModeTitlebarViewController
: NSTitlebarAccessoryViewController {
base::OnceClosure _view_will_appear_callback;
}
@end
@implementation ImmersiveModeTitlebarViewController
- (instancetype)initWithViewWillAppearCallback:
(base::OnceClosure)view_will_appear_callback {
if ((self = [super init])) {
_view_will_appear_callback = std::move(view_will_appear_callback);
}
return self;
}
- (void)viewWillAppear {
[super viewWillAppear];
// Resize the views and run the callback on the first call to this method. We
// will most likely be in the fullscreen transition window and we want our
// views to be displayed.
PropagateFrameSizeToViewsSubviews(self.view);
if (!_view_will_appear_callback.is_null()) {
// Triggers Views to display top chrome.
std::move(_view_will_appear_callback).Run();
}
// Sometimes AppKit incorrectly positions NSToolbarFullScreenWindow entirely
// offscreen (particularly when this is a out-of-process app shim). Toggling
// visibility when appearing in the right window seems to fix the positioning.
// Only toggle the visibility if fullScreenMinHeight is not zero though, as
// triggering the repositioning when the toolbar is set to auto hide would
// result in it being incorrectly positioned in that case.
if (remote_cocoa::IsNSToolbarFullScreenWindow(self.view.window) &&
self.fullScreenMinHeight != 0 && !self.hidden) {
self.hidden = YES;
self.hidden = NO;
}
}
@end
@interface ClearTitlebarViewController : NSTitlebarAccessoryViewController {
CGFloat _height;
}
@end
@implementation ClearTitlebarViewController
- (instancetype)initWithHeight:(CGFloat)height {
self = [super init];
if (self) {
_height = height;
}
return self;
}
- (void)viewWillAppear {
[super viewWillAppear];
NSSize size = self.view.frame.size;
size.height = _height;
[self.view setFrameSize:size];
// Hide the controller before it is appears but after the view's frame is
// set. This will extend the NSTitlebarAccessoryViewController mouse
// tracking area over the entirety of the window stopping the titlebar from
// auto hiding.
self.hidden = YES;
}
@end
// An NSView that will set the ImmersiveModeDelegate on the AppKit created
// window that ends up hosting this view via the
// NSTitlebarAccessoryViewController API.
@interface ImmersiveModeView : NSView
- (instancetype)initWithController:
(base::WeakPtr<remote_cocoa::ImmersiveModeController>)controller;
@end
@implementation ImmersiveModeView {
base::WeakPtr<remote_cocoa::ImmersiveModeController> _controller;
}
- (instancetype)initWithController:
(base::WeakPtr<remote_cocoa::ImmersiveModeController>)controller {
self = [super init];
if (self) {
_controller = std::move(controller);
}
return self;
}
- (void)viewWillMoveToWindow:(NSWindow*)window {
if (_controller) {
_controller->ImmersiveModeViewWillMoveToWindow(window);
}
}
@end
@interface ImmersiveModeWindowObserver : NSObject {
base::WeakPtr<remote_cocoa::ImmersiveModeController> _controller;
}
- (instancetype)initWithController:
(base::WeakPtr<remote_cocoa::ImmersiveModeController>)controller;
@end
@implementation ImmersiveModeWindowObserver
- (instancetype)initWithController:
(base::WeakPtr<remote_cocoa::ImmersiveModeController>)controller {
self = [super init];
if (self) {
_controller = std::move(controller);
}
return self;
}
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id>*)change
context:(void*)context {
if (![keyPath isEqualToString:@"visible"]) {
return;
}
BOOL visible = [change[NSKeyValueChangeNewKey] boolValue];
NSWindow* window = base::mac::ObjCCastStrict<NSWindow>(object);
if (visible) {
if (_controller) {
_controller->TitlebarLock();
}
return;
}
// Assume not-visible is a terminal state for an overlay child window. Also
// assume child windows will become not-visible before self is destroyed.
// These assumptions makes adding and removing the visible observer trival.
[window removeObserver:self forKeyPath:@"visible"];
if (_controller) {
_controller->TitlebarUnlock();
}
}
@end
namespace remote_cocoa {
bool IsNSToolbarFullScreenWindow(NSWindow* window) {
// TODO(bur): Investigate other approaches to detecting
// NSToolbarFullScreenWindow. This is a private class and the name could
// change.
return [window isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")];
}
ImmersiveModeController::ImmersiveModeController(NSWindow* browser_window,
NSWindow* overlay_window,
base::OnceClosure callback)
: browser_window_(browser_window),
overlay_window_(overlay_window),
weak_ptr_factory_(this) {
immersive_mode_window_observer_.reset([[ImmersiveModeWindowObserver alloc]
initWithController:weak_ptr_factory_.GetWeakPtr()]);
// A style of NSTitlebarSeparatorStyleAutomatic (default) will show a black
// line separator when removing the NSWindowStyleMaskFullSizeContentView style
// bit. We do not want a separator. Pre-macOS 11 there is no titlebar
// separator.
if (@available(macOS 11.0, *)) {
browser_window_.titlebarSeparatorStyle = NSTitlebarSeparatorStyleNone;
}
// Create a new NSTitlebarAccessoryViewController that will host the
// overlay_view_.
immersive_mode_titlebar_view_controller_.reset(
[[ImmersiveModeTitlebarViewController alloc]
initWithViewWillAppearCallback:std::move(callback)]);
// Create a NSWindow delegate that will be used to map the AppKit created
// NSWindow to the overlay view widget's NSWindow.
immersive_mode_mapper_.reset([[ImmersiveModeMapper alloc] init]);
immersive_mode_mapper_.get().originalHostingWindow = overlay_window_;
immersive_mode_titlebar_view_controller_.get().view =
[[ImmersiveModeView alloc]
initWithController:weak_ptr_factory_.GetWeakPtr()];
// Remove the content view from the overlay view widget's NSWindow. This
// view will be re-parented into the AppKit created NSWindow.
overlay_content_view_ = base::mac::ObjCCastStrict<BridgedContentView>(
overlay_window_.contentView);
[overlay_content_view_ retain];
[overlay_content_view_ removeFromSuperview];
// The original content view (top chrome) has been moved to the AppKit
// created NSWindow. Create a new content view but reuse the original bridge
// so that mouse drags are handled.
overlay_window_.contentView =
[[[BridgedContentView alloc] initWithBridge:overlay_content_view_.bridge
bounds:gfx::Rect()] autorelease];
// The overlay window will become a child of NSToolbarFullScreenWindow and sit
// above it in the z-order. Allow mouse events that are not handled by the
// BridgedContentView to passthrough the overlay window to the
// NSToolbarFullScreenWindow. This will allow the NSToolbarFullScreenWindow to
// become key when interacting with "top chrome".
overlay_window_.ignoresMouseEvents = YES;
// Add the overlay view to the accessory view controller getting ready to
// hand everything over to AppKit.
[immersive_mode_titlebar_view_controller_.get().view
addSubview:overlay_content_view_];
[overlay_content_view_ release];
immersive_mode_titlebar_view_controller_.get().layoutAttribute =
NSLayoutAttributeBottom;
thin_titlebar_view_controller_.reset(
[[NSTitlebarAccessoryViewController alloc] init]);
thin_titlebar_view_controller_.get().view =
[[[NSView alloc] init] autorelease];
thin_titlebar_view_controller_.get().view.wantsLayer = YES;
thin_titlebar_view_controller_.get().view.layer.backgroundColor =
NSColor.blackColor.CGColor;
thin_titlebar_view_controller_.get().layoutAttribute =
NSLayoutAttributeBottom;
thin_titlebar_view_controller_.get().fullScreenMinHeight =
kThinControllerHeight;
}
ImmersiveModeController::~ImmersiveModeController() {
// Remove the titlebar observer before moving the view.
immersive_mode_titlebar_observer_.reset();
// Rollback the view shuffling from enablement.
[thin_titlebar_view_controller_ removeFromParentViewController];
[overlay_content_view_ removeFromSuperview];
overlay_window_.contentView = overlay_content_view_;
[immersive_mode_titlebar_view_controller_ removeFromParentViewController];
[immersive_mode_titlebar_view_controller_.get().view release];
immersive_mode_titlebar_view_controller_.reset();
browser_window_.styleMask |= NSWindowStyleMaskFullSizeContentView;
if (@available(macOS 11.0, *)) {
browser_window_.titlebarSeparatorStyle = NSTitlebarSeparatorStyleAutomatic;
}
// Move sub-widgets back to the browser widget.
ReparentChildWindows(overlay_window_, browser_window_);
}
void ImmersiveModeController::Enable() {
DCHECK(!enabled_);
enabled_ = true;
[browser_window_ addTitlebarAccessoryViewController:
immersive_mode_titlebar_view_controller_];
// Move sub-widgets from the browser widget to the overlay widget so that
// they are rendered above the toolbar.
ObserveOverlayChildWindows();
ReparentChildWindows(browser_window_, overlay_window_);
[browser_window_
addTitlebarAccessoryViewController:thin_titlebar_view_controller_];
NSRect frame = thin_titlebar_view_controller_.get().view.frame;
frame.size.height = kThinControllerHeight;
thin_titlebar_view_controller_.get().view.frame = frame;
}
void ImmersiveModeController::OnTopViewBoundsChanged(const gfx::Rect& bounds) {
// Set the height of the AppKit fullscreen view. The width will be
// automatically handled by AppKit.
NSRect frame = NSRectFromCGRect(bounds.ToCGRect());
NSView* overlay_view = immersive_mode_titlebar_view_controller_.get().view;
NSSize size = overlay_view.frame.size;
size.height = frame.size.height;
[overlay_view setFrameSize:size];
UpdateToolbarVisibility(last_used_style_);
// If the toolbar is always visible, update the fullscreen min height.
// Also update the fullscreen min height if the toolbar auto hides, but only
// if the toolbar is currently revealed.
if (last_used_style_ == mojom::ToolbarVisibilityStyle::kAlways ||
(last_used_style_ == mojom::ToolbarVisibilityStyle::kAutohide &&
reveal_lock_count_ > 0)) {
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight =
immersive_mode_titlebar_view_controller_.get().view.frame.size.height;
}
}
void ImmersiveModeController::UpdateToolbarVisibility(
mojom::ToolbarVisibilityStyle style) {
// Remember the last used style for internal use of UpdateToolbarVisibility.
last_used_style_ = style;
// Only make changes if there are no outstanding reveal locks.
if (titlebar_lock_count_ > 0 || reveal_lock_count_ > 0) {
return;
}
switch (style) {
case mojom::ToolbarVisibilityStyle::kAlways:
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight =
immersive_mode_titlebar_view_controller_.get().view.frame.size.height;
thin_titlebar_view_controller_.get().hidden = YES;
browser_window_.styleMask &= ~NSWindowStyleMaskFullSizeContentView;
// Toggling the controller will allow the content view to resize below Top
// Chrome.
immersive_mode_titlebar_view_controller_.get().hidden = YES;
immersive_mode_titlebar_view_controller_.get().hidden = NO;
break;
case mojom::ToolbarVisibilityStyle::kAutohide:
immersive_mode_titlebar_view_controller_.get().hidden = NO;
// The thin titlebar controller keeps a tiny portion of the AppKit
// fullscreen NSWindow on screen as a workaround for
// https://crbug.com/1369643.
thin_titlebar_view_controller_.get().hidden = NO;
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight = 0;
browser_window_.styleMask |= NSWindowStyleMaskFullSizeContentView;
break;
case mojom::ToolbarVisibilityStyle::kNone:
thin_titlebar_view_controller_.get().hidden = YES;
immersive_mode_titlebar_view_controller_.get().hidden = YES;
break;
}
// Unpin the titlebar.
SetTitlebarPinned(false);
}
// This function will pin or unpin the titlebar (holder of the traffic
// lights). When the titlebar is pinned the titlebar will stay present on
// screen even if the mouse leaves the titlebar or Toolbar area. This is
// helpful when displaying sub-widgets. When the titlebar is not pinned it
// will reveal and auto-hide itself based on mouse movement (controlled by
// AppKit).
void ImmersiveModeController::SetTitlebarPinned(bool pinned) {
// Remove current, if any, clear controllers from the window. For some reason
// -removeFromParentViewController does not always remove the controller.
// Attempt to remove the current and any stale controllers.
for (NSTitlebarAccessoryViewController* c in browser_window_
.titlebarAccessoryViewControllers) {
if ([c isKindOfClass:[ClearTitlebarViewController class]]) {
[c removeFromParentViewController];
}
}
if (!pinned) {
clear_titlebar_view_controller_.reset();
return;
}
clear_titlebar_view_controller_.reset([[ClearTitlebarViewController alloc]
initWithHeight:browser_window_.contentView.frame.size.height -
kThinControllerHeight]);
clear_titlebar_view_controller_.get().view =
[[[NSView alloc] init] autorelease];
clear_titlebar_view_controller_.get().layoutAttribute =
NSLayoutAttributeBottom;
[browser_window_
addTitlebarAccessoryViewController:clear_titlebar_view_controller_];
}
void ImmersiveModeController::ObserveOverlayChildWindows() {
// Watch the overlay Widget for new child Widgets.
auto observe_window = [this](NSWindow* window) {
[window addObserver:immersive_mode_window_observer_
forKeyPath:@"visible"
options:NSKeyValueObservingOptionInitial |
NSKeyValueObservingOptionNew
context:nullptr];
};
NativeWidgetMacNSWindow* overlay_window =
base::mac::ObjCCastStrict<NativeWidgetMacNSWindow>(overlay_window_);
overlay_window.childWindowAddedHandler = ^(NSWindow* child) {
// Ignore non-visible children.
if (!child.visible) {
return;
}
observe_window(child);
};
}
void ImmersiveModeController::ReparentChildWindows(NSWindow* source,
NSWindow* target) {
NativeWidgetNSWindowBridge* source_bridge =
NativeWidgetNSWindowBridge::GetFromNativeWindow(source);
NativeWidgetNSWindowBridge* target_bridge =
NativeWidgetNSWindowBridge::GetFromNativeWindow(target);
// TODO(kerenzhu): DCHECK(source_bridge && target_bridge)
// Only in unittests the associated bridges might not exist.
if (source_bridge && target_bridge) {
source_bridge->MoveChildrenTo(target_bridge, /*anchored_only=*/true);
}
}
void ImmersiveModeController::TitlebarLock() {
titlebar_lock_count_++;
if (titlebar_fully_visible_) {
SetTitlebarPinned(true);
}
}
void ImmersiveModeController::TitlebarUnlock() {
if (--titlebar_lock_count_ < 1) {
SetTitlebarPinned(false);
}
DCHECK(titlebar_lock_count_ >= 0);
}
void ImmersiveModeController::RevealLock() {
reveal_lock_count_++;
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight =
immersive_mode_titlebar_view_controller_.get().view.frame.size.height;
}
void ImmersiveModeController::RevealUnlock() {
// Re-hide the toolbar if appropriate.
if (--reveal_lock_count_ < 1 &&
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight > 0 &&
last_used_style_ == mojom::ToolbarVisibilityStyle::kAutohide) {
immersive_mode_titlebar_view_controller_.get().fullScreenMinHeight = 0;
}
// Account for last_used_style_ changing to kAlways while a reveal lock was
// active.
if (reveal_lock_count_ < 1 &&
last_used_style_ == mojom::ToolbarVisibilityStyle::kAlways) {
UpdateToolbarVisibility(last_used_style_);
}
DCHECK(reveal_lock_count_ >= 0);
}
void ImmersiveModeController::ImmersiveModeViewWillMoveToWindow(
NSWindow* window) {
// AppKit hands this view controller over to a fullscreen transition window
// before we finally land at the NSToolbarFullScreenWindow. Add the frame
// observer only once we reach the NSToolbarFullScreenWindow.
if (remote_cocoa::IsNSToolbarFullScreenWindow(window)) {
// This window is created by AppKit. Make sure it doesn't have a delegate
// so we can use it for out own purposes.
DCHECK(!window.delegate);
window.delegate = immersive_mode_mapper_.get();
// Attach overlay_widget to NSToolbarFullScreen so that children are placed
// on top of the toolbar. When exitting fullscreeen, we don't re-parent the
// overlay window back to the browser window because it seems to trigger
// re-entrancy in AppKit and cause crash. This is safe because sub-widgets
// will be re-parented to the browser window and therefore the overlay
// window won't have any observable effect.
[window addChildWindow:overlay_window() ordered:NSWindowAbove];
NSView* view = GetNSTitlebarContainerViewFromWindow(window);
DCHECK(view);
// Create the titlebar observer. Observing can only start once the view has
// been fully re-parented into the AppKit fullscreen window.
immersive_mode_titlebar_observer_.reset(
[[ImmersiveModeTitlebarObserver alloc]
initWithController:weak_ptr_factory_.GetWeakPtr()
titlebarContainerView:view]);
}
}
void ImmersiveModeController::OnTitlebarFrameDidChange(NSRect frame) {
titlebar_fully_visible_ = frame.origin.y == 0;
// Find the overlay view's point on screen (bottom left).
NSPoint point_in_window = [overlay_content_view_ convertPoint:NSZeroPoint
toView:nil];
NSPoint point_on_screen =
[overlay_content_view_.window convertPointToScreen:point_in_window];
BOOL overlay_view_is_clipped = NO;
// This branch is only useful on macOS 11 and greater. macOS 10.15 and
// earlier move the window instead of clipping the view within the window.
// This allows the overlay window to appropriately track the overlay view.
if (@available(macOS 11.0, *)) {
// If the overlay view is clipped move the overlay window off screen. A
// clipped overlay view indicates the titlebar is hidden or is in
// transition AND the browser content view takes up the whole window
// ("Always Show Toolbar in Full Screen" is disabled). When we are in this
// state we don't want the overlay window on screen, otherwise it may mask
// input to the browser view. In all other cases will not enter this
// branch and the overlay window will be placed at the same coordinates as
// the overlay view.
if (overlay_content_view_.visibleRect.size.height !=
overlay_content_view_.frame.size.height) {
point_on_screen.y = -overlay_content_view_.frame.size.height;
overlay_view_is_clipped = YES;
}
}
if (!overlay_view_is_clipped) {
// If there are sub-windows and the titlebar is fully visible (a y origin
// of 0), pin the titlebar. This will prevent the titlebar from autohiding
// and causing the sub-windows from moving up when the mouse leaves top
// chrome.
if (!titlebar_frame_change_barrier_ && titlebar_fully_visible_ &&
titlebar_lock_count() > 0) {
// Add a barrier to prevent re-entry, which is a byproduct of
// TitlebarLock() and TitlebarUnlock().
base::AutoReset<bool> set_barrier(&titlebar_frame_change_barrier_, YES);
// This lock / unlock scheme is to force the titlebar to be pinned in
// place, which can only be done when the titlebar is fully visible.
// Existing sub-windows hold a lock, however since the titlebar isn't
// fully revealed until this point the existing locks don't actually pin
// the titlebar. The existing locks are still important for knowing when
// to unpin the titlebar. When all outstanding locks are released the
// titlebar be unpinned.
TitlebarLock();
TitlebarUnlock();
}
}
[overlay_window_ setFrameOrigin:point_on_screen];
}
} // namespace remote_cocoa