Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: chrome/browser/ui/cocoa/status_bubble_mac.mm

Issue 1103323002: Reland performance fixes for StatusBubbleMac. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Simpler fix/ Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/browser/ui/cocoa/status_bubble_mac_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/cocoa/status_bubble_mac.h" 5 #include "chrome/browser/ui/cocoa/status_bubble_mac.h"
6 6
7 #include <limits> 7 #include <limits>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
11 #include "base/debug/stack_trace.h"
11 #include "base/mac/mac_util.h" 12 #include "base/mac/mac_util.h"
12 #include "base/mac/scoped_block.h" 13 #include "base/mac/scoped_block.h"
13 #include "base/mac/sdk_forward_declarations.h" 14 #include "base/mac/sdk_forward_declarations.h"
14 #include "base/message_loop/message_loop.h" 15 #include "base/message_loop/message_loop.h"
15 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
16 #include "base/strings/sys_string_conversions.h" 17 #include "base/strings/sys_string_conversions.h"
17 #include "base/strings/utf_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h"
18 #import "chrome/browser/ui/cocoa/bubble_view.h" 19 #import "chrome/browser/ui/cocoa/bubble_view.h"
19 #include "chrome/browser/ui/elide_url.h" 20 #include "chrome/browser/ui/elide_url.h"
20 #include "net/base/net_util.h" 21 #include "net/base/net_util.h"
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 changes([NSAnimationContext currentContext]); 136 changes([NSAnimationContext currentContext]);
136 // At this point, -animationForKey should have been called by CoreAnimation 137 // At this point, -animationForKey should have been called by CoreAnimation
137 // to set up the animation to run. Verify this. 138 // to set up the animation to run. Verify this.
138 DCHECK(completionHandler_ == nil); 139 DCHECK(completionHandler_ == nil);
139 [NSAnimationContext endGrouping]; 140 [NSAnimationContext endGrouping];
140 } 141 }
141 } 142 }
142 143
143 @end 144 @end
144 145
146 // Mac implementation of the status bubble.
147 //
148 // Child windows interact with Spaces in interesting ways, so this code has to
149 // follow these rules:
150 //
151 // 1) NSWindows cannot have zero size. At times when the status bubble window
152 // has no specific size (for example, when hidden), its size is set to
153 // ui::kWindowSizeDeterminedLater.
154 //
155 // 2) Child window frames are in the coordinate space of the screen, not of the
156 // parent window. If a child window has its origin at (0, 0), Spaces will
157 // position it in the corner of the screen but group it with the parent
158 // window in Spaces. This causes Chrome windows to have a large (mostly
159 // blank) area in Spaces. To avoid this, child windows always have their
160 // origin set to the lower-left corner of the window.
161 //
162 // 3) Detached child windows may show up as top-level windows in Spaces. To
163 // avoid this, once the status bubble is Attach()ed to the parent, it is
164 // never detached (except in rare cases when reparenting to a fullscreen
165 // window).
166 //
167 // 4) To avoid unnecessary redraws, if a bubble is in the kBubbleHidden state,
168 // its size is always set to ui::kWindowSizeDeterminedLater. The proper
169 // width for the current URL or status text is not calculated until the
170 // bubble leaves the kBubbleHidden state.
171
145 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate) 172 StatusBubbleMac::StatusBubbleMac(NSWindow* parent, id delegate)
146 : parent_(parent), 173 : parent_(parent),
147 delegate_(delegate), 174 delegate_(delegate),
148 window_(nil), 175 window_(nil),
149 status_text_(nil), 176 status_text_(nil),
150 url_text_(nil), 177 url_text_(nil),
151 state_(kBubbleHidden), 178 state_(kBubbleHidden),
152 immediate_(false), 179 immediate_(false),
153 is_expanded_(false), 180 is_expanded_(false),
154 timer_factory_(this), 181 timer_factory_(this),
(...skipping 15 matching lines...) Expand all
170 } 197 }
171 198
172 void StatusBubbleMac::SetStatus(const base::string16& status) { 199 void StatusBubbleMac::SetStatus(const base::string16& status) {
173 SetText(status, false); 200 SetText(status, false);
174 } 201 }
175 202
176 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) { 203 void StatusBubbleMac::SetURL(const GURL& url, const std::string& languages) {
177 url_ = url; 204 url_ = url;
178 languages_ = languages; 205 languages_ = languages;
179 206
180 NSRect frame = [window_ frame]; 207 CGFloat bubble_width = NSWidth([window_ frame]);
181
182 // Reset frame size when bubble is hidden.
183 if (state_ == kBubbleHidden) { 208 if (state_ == kBubbleHidden) {
184 is_expanded_ = false; 209 // TODO(rohitrao): The window size is expected to be (1,1) whenever the
185 frame.size.width = NSWidth(CalculateWindowFrame(/*expand=*/false)); 210 // window is hidden, but the GPU bots are hitting cases where this is not
186 [window_ setFrame:frame display:NO]; 211 // true. Instead of enforcing this invariant with a DCHECK, add temporary
212 // logging to try and debug it and fix up the window size if needed.
213 // This logging is temporary and should be removed: crbug.com/467998
214 NSRect frame = [window_ frame];
215 if (!CGSizeEqualToSize(frame.size, ui::kWindowSizeDeterminedLater.size)) {
216 LOG(ERROR) << "Window size should be (1,1), but is instead ("
217 << frame.size.width << "," << frame.size.height << ")";
218 LOG(ERROR) << base::debug::StackTrace().ToString();
219 frame.size = ui::kWindowSizeDeterminedLater.size;
220 [window_ setFrame:frame display:NO];
221 }
222 bubble_width = NSWidth(CalculateWindowFrame(/*expand=*/false));
187 } 223 }
188 224
189 int text_width = static_cast<int>(NSWidth(frame) - 225 int text_width = static_cast<int>(bubble_width -
190 kBubbleViewTextPositionX - 226 kBubbleViewTextPositionX -
191 kTextPadding); 227 kTextPadding);
192 228
193 // Scale from view to window coordinates before eliding URL string. 229 // Scale from view to window coordinates before eliding URL string.
194 NSSize scaled_width = NSMakeSize(text_width, 0); 230 NSSize scaled_width = NSMakeSize(text_width, 0);
195 scaled_width = [[parent_ contentView] convertSize:scaled_width fromView:nil]; 231 scaled_width = [[parent_ contentView] convertSize:scaled_width fromView:nil];
196 text_width = static_cast<int>(scaled_width.width); 232 text_width = static_cast<int>(scaled_width.width);
197 NSFont* font = [[window_ contentView] font]; 233 NSFont* font = [[window_ contentView] font];
198 gfx::FontList font_list_chr( 234 gfx::FontList font_list_chr(
199 gfx::Font(base::SysNSStringToUTF8([font fontName]), [font pointSize])); 235 gfx::Font(base::SysNSStringToUTF8([font fontName]), [font pointSize]));
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 289
254 bool show = true; 290 bool show = true;
255 if ([*main length] > 0) 291 if ([*main length] > 0)
256 [[window_ contentView] setContent:*main]; 292 [[window_ contentView] setContent:*main];
257 else if ([*backup length] > 0) 293 else if ([*backup length] > 0)
258 [[window_ contentView] setContent:*backup]; 294 [[window_ contentView] setContent:*backup];
259 else 295 else
260 show = false; 296 show = false;
261 297
262 if (show) { 298 if (show) {
299 // Call StartShowing() first to update the current bubble state before
300 // calculating a new size.
301 StartShowing();
263 UpdateSizeAndPosition(); 302 UpdateSizeAndPosition();
264 StartShowing();
265 } else { 303 } else {
266 StartHiding(); 304 StartHiding();
267 } 305 }
268 } 306 }
269 307
270 void StatusBubbleMac::Hide() { 308 void StatusBubbleMac::Hide() {
271 CancelTimer(); 309 CancelTimer();
272 CancelExpandTimer(); 310 CancelExpandTimer();
273 is_expanded_ = false; 311 is_expanded_ = false;
274 312
275 bool fade_out = false; 313 bool fade_out = false;
276 if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) { 314 if (state_ == kBubbleHidingFadeOut || state_ == kBubbleShowingFadeIn) {
277 SetState(kBubbleHidingFadeOut); 315 SetState(kBubbleHidingFadeOut);
278 316
279 if (!immediate_) { 317 if (!immediate_) {
280 // An animation is in progress. Cancel it by starting a new animation. 318 // An animation is in progress. Cancel it by starting a new animation.
281 // Use kMinimumTimeInterval to set the opacity as rapidly as possible. 319 // Use kMinimumTimeInterval to set the opacity as rapidly as possible.
282 fade_out = true; 320 fade_out = true;
283 AnimateWindowAlpha(0.0, kMinimumTimeInterval); 321 AnimateWindowAlpha(0.0, kMinimumTimeInterval);
284 } 322 }
285 } 323 }
286 324
325 NSRect frame = CalculateWindowFrame(/*expand=*/false);
287 if (!fade_out) { 326 if (!fade_out) {
288 // No animation is in progress, so the opacity can be set directly. 327 // No animation is in progress, so the opacity can be set directly.
289 [window_ setAlphaValue:0.0]; 328 [window_ setAlphaValue:0.0];
290 SetState(kBubbleHidden); 329 SetState(kBubbleHidden);
330 frame.size = ui::kWindowSizeDeterminedLater.size;
291 } 331 }
292 332
293 // Stop any width animation and reset the bubble size. 333 // Stop any width animation and reset the bubble size.
294 if (!immediate_) { 334 if (!immediate_) {
295 [NSAnimationContext beginGrouping]; 335 [NSAnimationContext beginGrouping];
296 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval]; 336 [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval];
297 [[window_ animator] setFrame:CalculateWindowFrame(/*expand=*/false) 337 [[window_ animator] setFrame:frame display:NO];
298 display:NO];
299 [NSAnimationContext endGrouping]; 338 [NSAnimationContext endGrouping];
300 } else { 339 } else {
301 [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; 340 [window_ setFrame:frame display:NO];
302 } 341 }
303 342
304 [status_text_ release]; 343 [status_text_ release];
305 status_text_ = nil; 344 status_text_ = nil;
306 [url_text_ release]; 345 [url_text_ release];
307 url_text_ = nil; 346 url_text_ = nil;
308 } 347 }
309 348
310 void StatusBubbleMac::SetFrameAvoidingMouse( 349 void StatusBubbleMac::SetFrameAvoidingMouse(
311 NSRect window_frame, const gfx::Point& mouse_pos) { 350 NSRect window_frame, const gfx::Point& mouse_pos) {
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 [window_ orderFront:nil]; 478 [window_ orderFront:nil];
440 [parent_ addChildWindow:window_ ordered:NSWindowAbove]; 479 [parent_ addChildWindow:window_ ordered:NSWindowAbove];
441 480
442 [[window_ contentView] setThemeProvider:parent_]; 481 [[window_ contentView] setThemeProvider:parent_];
443 } 482 }
444 483
445 void StatusBubbleMac::Detach() { 484 void StatusBubbleMac::Detach() {
446 DCHECK(is_attached()); 485 DCHECK(is_attached());
447 486
448 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 . 487 // Magic setFrame: See http://crbug.com/58506 and http://crrev.com/3564021 .
449 [window_ setFrame:CalculateWindowFrame(/*expand=*/false) display:NO]; 488 // TODO(rohitrao): Does the frame size actually matter here? Can we always
489 // set it to kWindowSizeDeterminedLater?
490 NSRect frame = [window_ frame];
491 frame.size = ui::kWindowSizeDeterminedLater.size;
492 if (state_ != kBubbleHidden) {
493 frame = CalculateWindowFrame(/*expand=*/false);
494 }
495 [window_ setFrame:frame display:NO];
450 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ... 496 [parent_ removeChildWindow:window_]; // See crbug.com/28107 ...
451 [window_ orderOut:nil]; // ... and crbug.com/29054. 497 [window_ orderOut:nil]; // ... and crbug.com/29054.
452 498
453 [[window_ contentView] setThemeProvider:nil]; 499 [[window_ contentView] setThemeProvider:nil];
454 } 500 }
455 501
456 void StatusBubbleMac::AnimationDidStop() { 502 void StatusBubbleMac::AnimationDidStop() {
457 DCHECK([NSThread isMainThread]); 503 DCHECK([NSThread isMainThread]);
458 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut); 504 DCHECK(state_ == kBubbleShowingFadeIn || state_ == kBubbleHidingFadeOut);
459 DCHECK(is_attached()); 505 DCHECK(is_attached());
460 506
461 if (state_ == kBubbleShowingFadeIn) { 507 if (state_ == kBubbleShowingFadeIn) {
462 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity); 508 DCHECK_EQ([[window_ animator] alphaValue], kBubbleOpacity);
463 SetState(kBubbleShown); 509 SetState(kBubbleShown);
464 } else { 510 } else {
465 DCHECK_EQ([[window_ animator] alphaValue], 0.0); 511 DCHECK_EQ([[window_ animator] alphaValue], 0.0);
466 SetState(kBubbleHidden); 512 SetState(kBubbleHidden);
467 } 513 }
468 } 514 }
469 515
470 void StatusBubbleMac::SetState(StatusBubbleState state) { 516 void StatusBubbleMac::SetState(StatusBubbleState state) {
471 if (state == state_) 517 if (state == state_)
472 return; 518 return;
473 519
474 if (state == kBubbleHidden) { 520 if (state == kBubbleHidden) {
521 is_expanded_ = false;
522
475 // When hidden (with alpha of 0), make the window have the minimum size, 523 // When hidden (with alpha of 0), make the window have the minimum size,
476 // while still keeping the same origin. It's important to not set the 524 // while still keeping the same origin. It's important to not set the
477 // origin to 0,0 as that will cause the window to use more space in 525 // origin to 0,0 as that will cause the window to use more space in
478 // Expose/Mission Control. See http://crbug.com/81969. 526 // Expose/Mission Control. See http://crbug.com/81969.
479 // 527 //
480 // Also, doing it this way instead of detaching the window avoids bugs with 528 // Also, doing it this way instead of detaching the window avoids bugs with
481 // Spaces and Cmd-`. See http://crbug.com/31821 and http://crbug.com/61629. 529 // Spaces and Cmd-`. See http://crbug.com/31821 and http://crbug.com/61629.
482 NSRect frame = [window_ frame]; 530 NSRect frame = [window_ frame];
483 frame.size = ui::kWindowSizeDeterminedLater.size; 531 frame.size = ui::kWindowSizeDeterminedLater.size;
484 [window_ setFrame:frame display:YES]; 532 [window_ setFrame:frame display:YES];
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 [NSAnimationContext beginGrouping]; 758 [NSAnimationContext beginGrouping];
711 [[NSAnimationContext currentContext] setDuration:kExpansionDurationSeconds]; 759 [[NSAnimationContext currentContext] setDuration:kExpansionDurationSeconds];
712 [[window_ animator] setFrame:actual_window_frame display:YES]; 760 [[window_ animator] setFrame:actual_window_frame display:YES];
713 [NSAnimationContext endGrouping]; 761 [NSAnimationContext endGrouping];
714 } 762 }
715 763
716 void StatusBubbleMac::UpdateSizeAndPosition() { 764 void StatusBubbleMac::UpdateSizeAndPosition() {
717 if (!window_) 765 if (!window_)
718 return; 766 return;
719 767
768 // There is no need to update the size if the bubble is hidden.
769 if (state_ == kBubbleHidden) {
770 // Verify that hidden bubbles always have size equal to
771 // ui::kWindowSizeDeterminedLater.
772
773 // TODO(rohitrao): The GPU bots are hitting cases where this is not true.
774 // Instead of enforcing this invariant with a DCHECK, add temporary logging
775 // to try and debug it and fix up the window size if needed.
776 // This logging is temporary and should be removed: crbug.com/467998
777 NSRect frame = [window_ frame];
778 if (!CGSizeEqualToSize(frame.size, ui::kWindowSizeDeterminedLater.size)) {
779 LOG(ERROR) << "Window size should be (1,1), but is instead ("
780 << frame.size.width << "," << frame.size.height << ")";
781 LOG(ERROR) << base::debug::StackTrace().ToString();
782 frame.size = ui::kWindowSizeDeterminedLater.size;
783 }
784
785 // During the fullscreen animation, the parent window's origin may change
786 // without updating the status bubble. To avoid animation glitches, always
787 // update the bubble's origin to match the parent's, even when hidden.
788 frame.origin = [parent_ convertBaseToScreen:([parent_ frame].origin)];
rohitrao (ping after 24h) 2015/04/27 22:26:09 I realized that the actual origin doesn't matter q
789 [window_ setFrame:frame display:YES];
erikchen 2015/04/27 22:33:26 display:YES forces a redraw, even if the origin ha
790 return;
791 }
792
720 SetFrameAvoidingMouse(CalculateWindowFrame(/*expand=*/false), 793 SetFrameAvoidingMouse(CalculateWindowFrame(/*expand=*/false),
721 GetMouseLocation()); 794 GetMouseLocation());
722 } 795 }
723 796
724 void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) { 797 void StatusBubbleMac::SwitchParentWindow(NSWindow* parent) {
725 DCHECK(parent); 798 DCHECK(parent);
726 DCHECK(is_attached()); 799 DCHECK(is_attached());
727 800
728 Detach(); 801 Detach();
729 parent_ = parent; 802 parent_ = parent;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
775 } 848 }
776 849
777 // Round the top corners when the bubble is below the parent window. 850 // Round the top corners when the bubble is below the parent window.
778 if (NSMinY(window_frame) < NSMinY(parent_frame)) { 851 if (NSMinY(window_frame) < NSMinY(parent_frame)) {
779 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner; 852 corner_flags |= kRoundedTopLeftCorner | kRoundedTopRightCorner;
780 } 853 }
781 } 854 }
782 855
783 return corner_flags; 856 return corner_flags;
784 } 857 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/ui/cocoa/status_bubble_mac_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698