From 35b1eb2a2e8bca7732421539e9094813fc7e77e2 Mon Sep 17 00:00:00 2001 From: "shess@chromium.org" Date: Wed, 7 Jul 2010 22:20:14 +0000 Subject: [Mac] Don't change state from stop to reload when hovered. Hold the reload button state change from stop to reload until the mouse exits. Also short-circuit the stop message when the real state should be reload. Also prevent multi-clicks from sending multiple actions. Toolbar.xib: reload button made kind of ReloadButton. BUG=47184 TEST=Browse to a slow page, mouse over stop button. Should not go to reload button when throbber (in tab) stops. TEST=After page loaded, click stop. Should not crash. TEST=Hover over reload button. Command-r should start a reload and change the button to stop button. Review URL: http://codereview.chromium.org/2847051 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51787 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/cocoa/browser_window_cocoa.mm | 2 +- chrome/browser/cocoa/browser_window_controller.h | 4 +- chrome/browser/cocoa/browser_window_controller.mm | 4 +- chrome/browser/cocoa/gradient_button_cell.h | 6 +- chrome/browser/cocoa/reload_button.h | 44 +++++ chrome/browser/cocoa/reload_button.mm | 116 +++++++++++++ chrome/browser/cocoa/reload_button_unittest.mm | 187 +++++++++++++++++++++ chrome/browser/cocoa/test_event_utils.h | 8 +- chrome/browser/cocoa/test_event_utils.mm | 24 ++- chrome/browser/cocoa/toolbar_controller.h | 11 +- chrome/browser/cocoa/toolbar_controller.mm | 13 +- .../browser/cocoa/toolbar_controller_unittest.mm | 4 +- 12 files changed, 396 insertions(+), 27 deletions(-) create mode 100644 chrome/browser/cocoa/reload_button.h create mode 100644 chrome/browser/cocoa/reload_button.mm create mode 100644 chrome/browser/cocoa/reload_button_unittest.mm (limited to 'chrome/browser/cocoa') diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 556263b..274f5b8 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -219,7 +219,7 @@ void BrowserWindowCocoa::SetFocusToLocationBar(bool select_all) { } void BrowserWindowCocoa::UpdateReloadStopState(bool is_loading, bool force) { - [controller_ setIsLoading:is_loading ? YES : NO]; + [controller_ setIsLoading:is_loading force:force]; } void BrowserWindowCocoa::UpdateToolbar(TabContents* contents, diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index aaf843a..5882363 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -175,7 +175,9 @@ class TabStripModelObserverBridge; - (NSRect)selectedTabGrowBoxRect; // Called to tell the selected tab to update its loading state. -- (void)setIsLoading:(BOOL)isLoading; +// |force| is set if the update is due to changing tabs, as opposed to +// the page-load finishing. See comment in reload_button.h. +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force; // Brings this controller's window to the front. - (void)activate; diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index 96b0245..aa7b325 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -1104,8 +1104,8 @@ return [tabStripController_ selectedTabView]; } -- (void)setIsLoading:(BOOL)isLoading { - [toolbarController_ setIsLoading:isLoading]; +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { + [toolbarController_ setIsLoading:isLoading force:force]; } // Make the location bar the first responder, if possible. diff --git a/chrome/browser/cocoa/gradient_button_cell.h b/chrome/browser/cocoa/gradient_button_cell.h index 72b3c30..44ce2c1 100644 --- a/chrome/browser/cocoa/gradient_button_cell.h +++ b/chrome/browser/cocoa/gradient_button_cell.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_COCOA_CHROMIUM_BUTTON_CELL_H_ -#define CHROME_BROWSER_COCOA_CHROMIUM_BUTTON_CELL_H_ +#ifndef CHROME_BROWSER_COCOA_GRADIENT_BUTTON_CELL_H_ +#define CHROME_BROWSER_COCOA_GRADIENT_BUTTON_CELL_H_ #import @@ -73,4 +73,4 @@ typedef NSInteger ButtonType; - (BOOL)isMouseInside; @end -#endif // CHROME_BROWSER_COCOA_CHROMIUM_BUTTON_CELL_H_ +#endif // CHROME_BROWSER_COCOA_GRADIENT_BUTTON_CELL_H_ diff --git a/chrome/browser/cocoa/reload_button.h b/chrome/browser/cocoa/reload_button.h new file mode 100644 index 0000000..c26f047 --- /dev/null +++ b/chrome/browser/cocoa/reload_button.h @@ -0,0 +1,44 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_COCOA_RELOAD_BUTTON_H_ +#define CHROME_BROWSER_COCOA_RELOAD_BUTTON_H_ + +#import + +#import "base/scoped_nsobject.h" + +// NSButton subclass which defers certain state changes when the mouse +// is hovering over it. + +@interface ReloadButton : NSButton { + @private + // Tracks whether the mouse is hovering for purposes of not making + // unexpected state changes. + BOOL isMouseInside_; + scoped_nsobject trackingArea_; + + // Set when reload mode is requested, but not forced, and the mouse + // is hovering. + BOOL pendingReloadMode_; +} + +// Returns YES if the mouse is currently inside the bounds. +- (BOOL)isMouseInside; + +// Update the button to be a reload button or stop button depending on +// |isLoading|. If |force|, always sets the indicated mode. If +// |!force|, and the mouse is over the button, defer the transition +// from stop button to reload button until the mouse has left the +// button. This prevents an inadvertent click _just_ as the state +// changes. +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force; + +@end + +@interface ReloadButton (PrivateTestingMethods) +- (NSTrackingArea*)trackingArea; +@end + +#endif // CHROME_BROWSER_COCOA_RELOAD_BUTTON_H_ diff --git a/chrome/browser/cocoa/reload_button.mm b/chrome/browser/cocoa/reload_button.mm new file mode 100644 index 0000000..53e6c58 --- /dev/null +++ b/chrome/browser/cocoa/reload_button.mm @@ -0,0 +1,116 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "chrome/browser/cocoa/reload_button.h" + +#include "base/nsimage_cache_mac.h" +#include "chrome/app/chrome_dll_resource.h" + +namespace { + +NSString* const kReloadImageName = @"reload_Template.pdf"; +NSString* const kStopImageName = @"stop_Template.pdf"; + +} // namespace + +@implementation ReloadButton + +- (void)dealloc { + if (trackingArea_) { + [self removeTrackingArea:trackingArea_]; + trackingArea_.reset(); + } + [super dealloc]; +} + +- (void)updateTrackingAreas { + // If the mouse is hovering when the tracking area is updated, the + // control could end up locked into inappropriate behavior for + // awhile, so unwind state. + if (isMouseInside_) + [self mouseExited:nil]; + + if (trackingArea_) { + [self removeTrackingArea:trackingArea_]; + trackingArea_.reset(); + } + trackingArea_.reset([[NSTrackingArea alloc] + initWithRect:[self bounds] + options:(NSTrackingMouseEnteredAndExited | + NSTrackingActiveInActiveApp) + owner:self + userInfo:nil]); + [self addTrackingArea:trackingArea_]; +} + +- (void)awakeFromNib { + [self updateTrackingAreas]; + + // Don't allow multi-clicks, because the user probably wouldn't ever + // want to stop+reload or reload+stop. + [self setIgnoresMultiClick:YES]; +} + +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { + pendingReloadMode_ = NO; + + // Can always transition to stop mode. Only transition to reload + // mode if forced or if the mouse isn't hovering. Otherwise, note + // that reload mode is desired and make no change. + if (isLoading) { + [self setImage:nsimage_cache::ImageNamed(kStopImageName)]; + [self setTag:IDC_STOP]; + } else if (force || ![self isMouseInside]) { + [self setImage:nsimage_cache::ImageNamed(kReloadImageName)]; + [self setTag:IDC_RELOAD]; + } else if ([self tag] == IDC_STOP) { + pendingReloadMode_ = YES; + } +} + +- (BOOL)sendAction:(SEL)theAction to:(id)theTarget { + if ([self tag] == IDC_STOP) { + // The stop command won't be valid after the attempt to change + // back to reload. But it "worked", so short-circuit it. + const BOOL ret = + pendingReloadMode_ ? YES : [super sendAction:theAction to:theTarget]; + + // When the stop is processed, immediately change to reload mode, + // even though the IPC still has to bounce off the renderer and + // back before the regular |-setIsLoaded:force:| will be called. + // [This is how views and gtk do it.] + if (ret) + [self setIsLoading:NO force:YES]; + + return ret; + } + + return [super sendAction:theAction to:theTarget]; +} + +- (void)mouseEntered:(NSEvent*)theEvent { + isMouseInside_ = YES; +} + +- (void)mouseExited:(NSEvent*)theEvent { + isMouseInside_ = NO; + + // Reload mode was requested during the hover. + if (pendingReloadMode_) + [self setIsLoading:NO force:YES]; +} + +- (BOOL)isMouseInside { + return trackingArea_ && isMouseInside_; +} + +@end // ReloadButton + +@implementation ReloadButton (Testing) + +- (NSTrackingArea*)trackingArea { + return trackingArea_; +} + +@end diff --git a/chrome/browser/cocoa/reload_button_unittest.mm b/chrome/browser/cocoa/reload_button_unittest.mm new file mode 100644 index 0000000..64deb96 --- /dev/null +++ b/chrome/browser/cocoa/reload_button_unittest.mm @@ -0,0 +1,187 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#import "chrome/browser/cocoa/reload_button.h" + +#include "base/scoped_nsobject.h" +#include "chrome/app/chrome_dll_resource.h" +#import "chrome/browser/cocoa/cocoa_test_helper.h" +#import "chrome/browser/cocoa/test_event_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" +#import "third_party/ocmock/OCMock/OCMock.h" + +@protocol TargetActionMock +- (void)anAction:(id)sender; +@end + +namespace { + +class ReloadButtonTest : public CocoaTest { + public: + ReloadButtonTest() { + NSRect frame = NSMakeRect(0, 0, 20, 20); + scoped_nsobject button( + [[ReloadButton alloc] initWithFrame:frame]); + button_ = button.get(); + + // Set things up so unit tests have a reliable baseline. + [button_ setTag:IDC_RELOAD]; + [button_ awakeFromNib]; + + [[test_window() contentView] addSubview:button_]; + } + + ReloadButton* button_; +}; + +TEST_VIEW(ReloadButtonTest, button_) + +// Test that mouse-tracking is setup and does the right thing. +TEST_F(ReloadButtonTest, IsMouseInside) { + EXPECT_TRUE([[button_ trackingAreas] containsObject:[button_ trackingArea]]); + + EXPECT_FALSE([button_ isMouseInside]); + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ mouseExited:nil]; +} + +// Verify that multiple clicks do not result in multiple messages to +// the target. +TEST_F(ReloadButtonTest, IgnoredMultiClick) { + id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; + [button_ setTarget:mock_target]; + [button_ setAction:@selector(anAction:)]; + + // Expect the action once. + [[mock_target expect] anAction:button_]; + + const std::pair click_one = + test_event_utils::MouseClickInView(button_, 1); + const std::pair click_two = + test_event_utils::MouseClickInView(button_, 2); + [NSApp postEvent:click_one.second atStart:YES]; + [button_ mouseDown:click_one.first]; + [NSApp postEvent:click_two.second atStart:YES]; + [button_ mouseDown:click_two.first]; + + [button_ setTarget:nil]; +} + +// Test that when forcing the mode, it takes effect immediately, +// regardless of whether the mouse is hovering. +TEST_F(ReloadButtonTest, SetIsLoadingForce) { + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_RELOAD); + + // Changes to stop immediately. + [button_ setIsLoading:YES force:YES]; + EXPECT_EQ([button_ tag], IDC_STOP); + + // Changes to reload immediately. + [button_ setIsLoading:NO force:YES]; + EXPECT_EQ([button_ tag], IDC_RELOAD); + + // Changes to stop immediately when the mouse is hovered, and + // doesn't change when the mouse exits. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:YES force:YES]; + EXPECT_EQ([button_ tag], IDC_STOP); + [button_ mouseExited:nil]; + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_STOP); + + // Changes to reload immediately when the mouse is hovered, and + // doesn't change when the mouse exits. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:NO force:YES]; + EXPECT_EQ([button_ tag], IDC_RELOAD); + [button_ mouseExited:nil]; + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_RELOAD); +} + +// Test that without force, stop mode is set immediately, but reload +// is affected by the hover status. +TEST_F(ReloadButtonTest, SetIsLoadingNoForce) { + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_RELOAD); + + // Changes to stop immediately when the mouse is not hovering. + [button_ setIsLoading:YES force:NO]; + EXPECT_EQ([button_ tag], IDC_STOP); + + // Changes to reload immediately when the mouse is not hovering. + [button_ setIsLoading:NO force:NO]; + EXPECT_EQ([button_ tag], IDC_RELOAD); + + // Changes to stop immediately when the mouse is hovered, and + // doesn't change when the mouse exits. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:YES force:NO]; + EXPECT_EQ([button_ tag], IDC_STOP); + [button_ mouseExited:nil]; + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_STOP); + + // Does not change to reload immediately when the mouse is hovered, + // changes when the mouse exits. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:NO force:NO]; + EXPECT_EQ([button_ tag], IDC_STOP); + [button_ mouseExited:nil]; + EXPECT_FALSE([button_ isMouseInside]); + EXPECT_EQ([button_ tag], IDC_RELOAD); +} + +// Test that pressing stop after reload mode has been requested +// doesn't forward the stop message. +TEST_F(ReloadButtonTest, StopAfterReloadSet) { + id mock_target = [OCMockObject mockForProtocol:@protocol(TargetActionMock)]; + [button_ setTarget:mock_target]; + [button_ setAction:@selector(anAction:)]; + + EXPECT_FALSE([button_ isMouseInside]); + + // Get to stop mode. + [button_ setIsLoading:YES force:YES]; + EXPECT_EQ([button_ tag], IDC_STOP); + + // Expect the action once. + [[mock_target expect] anAction:button_]; + + // Clicking in stop mode should send the action and transition to + // reload mode. + const std::pair click = + test_event_utils::MouseClickInView(button_, 1); + [NSApp postEvent:click.second atStart:YES]; + [button_ mouseDown:click.first]; + EXPECT_EQ([button_ tag], IDC_RELOAD); + + // Get back to stop mode. + [button_ setIsLoading:YES force:YES]; + EXPECT_EQ([button_ tag], IDC_STOP); + + // If hover prevented reload mode immediately taking effect, clicks + // should not send any action, but should still transition to reload + // mode. + [button_ mouseEntered:nil]; + EXPECT_TRUE([button_ isMouseInside]); + [button_ setIsLoading:NO force:NO]; + EXPECT_EQ([button_ tag], IDC_STOP); + [NSApp postEvent:click.second atStart:YES]; + [button_ mouseDown:click.first]; + EXPECT_EQ([button_ tag], IDC_RELOAD); + + [button_ setTarget:nil]; +} + +} // namespace diff --git a/chrome/browser/cocoa/test_event_utils.h b/chrome/browser/cocoa/test_event_utils.h index 6b2eba5..f81fc6c 100644 --- a/chrome/browser/cocoa/test_event_utils.h +++ b/chrome/browser/cocoa/test_event_utils.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_COCOA_TEST_EVENT_UTILS_H_ #define CHROME_BROWSER_COCOA_TEST_EVENT_UTILS_H_ +#include + #import #include "base/logging.h" @@ -35,7 +37,11 @@ NSEvent* MouseEventAtPoint(NSPoint point, NSEventType type, NSEvent* LeftMouseDownAtPoint(NSPoint point); NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window); +// Return a mouse down and an up event with the given |clickCount| at +// |view|'s midpoint. +std::pair MouseClickInView(NSView* view, + NSUInteger clickCount); + } // namespace test_event_utils #endif // CHROME_BROWSER_COCOA_TEST_EVENT_UTILS_H_ - diff --git a/chrome/browser/cocoa/test_event_utils.mm b/chrome/browser/cocoa/test_event_utils.mm index 23b0535..8016111 100644 --- a/chrome/browser/cocoa/test_event_utils.mm +++ b/chrome/browser/cocoa/test_event_utils.mm @@ -49,20 +49,38 @@ NSEvent* MakeMouseEvent(NSEventType type, NSUInteger modifiers) { return MouseEventAtPoint(NSMakePoint(0, 0), type, modifiers); } -NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window) { - return [NSEvent mouseEventWithType:NSLeftMouseDown +static NSEvent* MouseEventAtPointInWindow(NSPoint point, + NSEventType type, + NSWindow* window, + NSUInteger clickCount) { + return [NSEvent mouseEventWithType:type location:point modifierFlags:0 timestamp:0 windowNumber:[window windowNumber] context:nil eventNumber:0 - clickCount:1 + clickCount:clickCount pressure:1.0]; } +NSEvent* LeftMouseDownAtPointInWindow(NSPoint point, NSWindow* window) { + return MouseEventAtPointInWindow(point, NSLeftMouseDown, window, 1); +} + NSEvent* LeftMouseDownAtPoint(NSPoint point) { return LeftMouseDownAtPointInWindow(point, nil); } +std::pair MouseClickInView(NSView* view, + NSUInteger clickCount) { + const NSRect bounds = [view convertRect:[view bounds] toView:nil]; + const NSPoint mid_point = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); + NSEvent* down = MouseEventAtPointInWindow(mid_point, NSLeftMouseDown, + [view window], clickCount); + NSEvent* up = MouseEventAtPointInWindow(mid_point, NSLeftMouseUp, + [view window], clickCount); + return std::make_pair(down, up); +} + } // namespace test_event_utils diff --git a/chrome/browser/cocoa/toolbar_controller.h b/chrome/browser/cocoa/toolbar_controller.h index 313ef4e..be27c42 100644 --- a/chrome/browser/cocoa/toolbar_controller.h +++ b/chrome/browser/cocoa/toolbar_controller.h @@ -32,6 +32,7 @@ class MenuDelegate; class PrefObserverBridge; } class Profile; +@class ReloadButton; class TabContents; class ToolbarModel; class WrenchMenuModel; @@ -49,7 +50,7 @@ class WrenchMenuModel; // corresponding enum in the unit tests. IBOutlet DelayedMenuButton* backButton_; IBOutlet DelayedMenuButton* forwardButton_; - IBOutlet NSButton* reloadButton_; + IBOutlet ReloadButton* reloadButton_; IBOutlet NSButton* homeButton_; IBOutlet MenuButton* wrenchButton_; IBOutlet AutocompleteTextField* locationBar_; @@ -129,9 +130,11 @@ class WrenchMenuModel; // Sets whether or not the current page in the frontmost tab is bookmarked. - (void)setStarredState:(BOOL)isStarred; -// Called to update the loading state. Handles updating the go/stop button -// state. -- (void)setIsLoading:(BOOL)isLoading; +// Called to update the loading state. Handles updating the go/stop +// button state. |force| is set if the update is due to changing +// tabs, as opposed to the page-load finishing. See comment in +// reload_button.h. +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force; // Allow turning off the toolbar (but we may keep the location bar without a // surrounding toolbar). If |toolbar| is YES, the value of |hasLocationBar| is diff --git a/chrome/browser/cocoa/toolbar_controller.mm b/chrome/browser/cocoa/toolbar_controller.mm index 4823f31..7487241 100644 --- a/chrome/browser/cocoa/toolbar_controller.mm +++ b/chrome/browser/cocoa/toolbar_controller.mm @@ -29,6 +29,7 @@ #import "chrome/browser/cocoa/location_bar/location_bar_view_mac.h" #import "chrome/browser/cocoa/menu_button.h" #import "chrome/browser/cocoa/menu_controller.h" +#import "chrome/browser/cocoa/reload_button.h" #import "chrome/browser/cocoa/toolbar_view.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/pref_service.h" @@ -412,16 +413,8 @@ class PrefObserverBridge : public NotificationObserver { locationBarView_->SetStarred(isStarred ? true : false); } -- (void)setIsLoading:(BOOL)isLoading { - NSString* imageName = kReloadButtonReloadImageName; - NSInteger tag = IDC_RELOAD; - if (isLoading) { - imageName = kReloadButtonStopImageName; - tag = IDC_STOP; - } - NSImage* stopStartImage = nsimage_cache::ImageNamed(imageName); - [reloadButton_ setImage:stopStartImage]; - [reloadButton_ setTag:tag]; +- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { + [reloadButton_ setIsLoading:isLoading force:force]; } - (void)setHasToolbar:(BOOL)toolbar hasLocationBar:(BOOL)locBar { diff --git a/chrome/browser/cocoa/toolbar_controller_unittest.mm b/chrome/browser/cocoa/toolbar_controller_unittest.mm index b601e69..c5f155d 100644 --- a/chrome/browser/cocoa/toolbar_controller_unittest.mm +++ b/chrome/browser/cocoa/toolbar_controller_unittest.mm @@ -154,9 +154,9 @@ TEST_F(ToolbarControllerTest, LoadingState) { // IDC_RELOAD. When loading, it should be IDC_STOP. NSButton* reload = [[bar_ toolbarViews] objectAtIndex:kReloadIndex]; EXPECT_EQ([reload tag], IDC_RELOAD); - [bar_ setIsLoading:YES]; + [bar_ setIsLoading:YES force:YES]; EXPECT_EQ([reload tag], IDC_STOP); - [bar_ setIsLoading:NO]; + [bar_ setIsLoading:NO force:YES]; EXPECT_EQ([reload tag], IDC_RELOAD); } -- cgit v1.1