Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 #import "browser_actions_controller.h" | 5 #import "browser_actions_controller.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/browser/browser.h" | 10 #include "chrome/browser/browser.h" |
| 11 #include "chrome/browser/cocoa/extensions/browser_action_button.h" | 11 #include "chrome/browser/cocoa/extensions/browser_action_button.h" |
| 12 #include "chrome/browser/cocoa/extensions/browser_actions_container_view.h" | 12 #include "chrome/browser/cocoa/extensions/browser_actions_container_view.h" |
| 13 #include "chrome/browser/cocoa/extensions/extension_popup_controller.h" | 13 #include "chrome/browser/cocoa/extensions/extension_popup_controller.h" |
| 14 #include "chrome/browser/extensions/extension_browser_event_router.h" | 14 #include "chrome/browser/extensions/extension_browser_event_router.h" |
| 15 #include "chrome/browser/extensions/extension_toolbar_model.h" | |
| 15 #include "chrome/browser/extensions/extensions_service.h" | 16 #include "chrome/browser/extensions/extensions_service.h" |
| 16 #include "chrome/browser/profile.h" | 17 #include "chrome/browser/profile.h" |
| 17 #include "chrome/browser/tab_contents/tab_contents.h" | 18 #include "chrome/browser/tab_contents/tab_contents.h" |
| 18 #include "chrome/common/notification_observer.h" | 19 #include "chrome/common/notification_observer.h" |
| 19 #include "chrome/common/notification_registrar.h" | 20 #include "chrome/common/notification_registrar.h" |
| 20 | 21 |
| 21 // The padding between browser action buttons. | 22 // The padding between browser action buttons. |
| 22 extern const CGFloat kBrowserActionButtonPadding = 3; | 23 extern const CGFloat kBrowserActionButtonPadding = 3; |
| 23 | 24 |
| 24 NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged"; | 25 NSString* const kBrowserActionsChangedNotification = @"BrowserActionsChanged"; |
| 25 | 26 |
| 26 @interface BrowserActionsController(Private) | 27 @interface BrowserActionsController(Private) |
| 27 - (void)createActionButtonForExtension:(Extension*)extension; | 28 - (void)createActionButtonForExtension:(Extension*)extension |
| 29 withIndex:(int)index; | |
| 28 - (void)removeActionButtonForExtension:(Extension*)extension; | 30 - (void)removeActionButtonForExtension:(Extension*)extension; |
| 29 - (void)repositionActionButtons; | 31 - (void)repositionActionButtons; |
| 30 - (int)currentTabId; | 32 - (int)currentTabId; |
| 31 @end | 33 @end |
| 32 | 34 |
| 33 // A helper class to proxy extension notifications to the view controller's | 35 // A helper class to proxy extension notifications to the view controller's |
| 34 // appropriate methods. | 36 // appropriate methods. |
| 35 class ExtensionsServiceObserverBridge : public NotificationObserver { | 37 class ExtensionsServiceObserverBridge : public NotificationObserver, |
| 38 public ExtensionToolbarModel::Observer { | |
| 36 public: | 39 public: |
| 37 ExtensionsServiceObserverBridge(BrowserActionsController* owner, | 40 ExtensionsServiceObserverBridge(BrowserActionsController* owner, |
| 38 Profile* profile) : owner_(owner) { | 41 Profile* profile) : owner_(owner) { |
| 39 registrar_.Add(this, NotificationType::EXTENSION_LOADED, | |
| 40 Source<Profile>(profile)); | |
| 41 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, | |
| 42 Source<Profile>(profile)); | |
| 43 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, | |
| 44 Source<Profile>(profile)); | |
| 45 registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, | 42 registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, |
| 46 Source<Profile>(profile)); | 43 Source<Profile>(profile)); |
| 47 } | 44 } |
| 48 | 45 |
| 49 // Runs |owner_|'s method corresponding to the event type received from the | |
| 50 // notification system. | |
| 51 // Overridden from NotificationObserver. | 46 // Overridden from NotificationObserver. |
| 52 void Observe(NotificationType type, | 47 void Observe(NotificationType type, |
| 53 const NotificationSource& source, | 48 const NotificationSource& source, |
| 54 const NotificationDetails& details) { | 49 const NotificationDetails& details) { |
| 55 switch (type.value) { | 50 switch (type.value) { |
| 56 case NotificationType::EXTENSION_LOADED: { | |
| 57 Extension* extension = Details<Extension>(details).ptr(); | |
| 58 [owner_ createActionButtonForExtension:extension]; | |
| 59 [owner_ browserActionVisibilityHasChanged]; | |
| 60 break; | |
| 61 } | |
| 62 case NotificationType::EXTENSION_UNLOADED: | |
| 63 case NotificationType::EXTENSION_UNLOADED_DISABLED: { | |
| 64 Extension* extension = Details<Extension>(details).ptr(); | |
| 65 [owner_ removeActionButtonForExtension:extension]; | |
| 66 [owner_ browserActionVisibilityHasChanged]; | |
| 67 break; | |
| 68 } | |
| 69 case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: { | 51 case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: { |
| 70 ExtensionPopupController* popup = [ExtensionPopupController popup]; | 52 ExtensionPopupController* popup = [ExtensionPopupController popup]; |
| 71 if (popup && ![popup isClosing]) | 53 if (popup && ![popup isClosing]) |
| 72 [popup close]; | 54 [popup close]; |
| 73 | 55 |
| 74 break; | 56 break; |
| 75 } | 57 } |
| 76 default: | 58 default: |
| 77 NOTREACHED() << L"Unexpected notification"; | 59 NOTREACHED() << L"Unexpected notification"; |
| 78 } | 60 } |
| 79 } | 61 } |
| 80 | 62 |
| 63 // ExtensionToolbarModel::Observer implementation. | |
| 64 void BrowserActionAdded(Extension* extension, int index) { | |
| 65 [owner_ createActionButtonForExtension:extension withIndex:index]; | |
| 66 } | |
| 67 | |
| 68 void BrowserActionRemoved(Extension* extension) { | |
| 69 [owner_ removeActionButtonForExtension:extension]; | |
| 70 } | |
| 71 | |
| 81 private: | 72 private: |
| 82 // The object we need to inform when we get a notification. Weak. Owns us. | 73 // The object we need to inform when we get a notification. Weak. Owns us. |
| 83 BrowserActionsController* owner_; | 74 BrowserActionsController* owner_; |
| 84 | 75 |
| 85 // Used for registering to receive notifications and automatic clean up. | 76 // Used for registering to receive notifications and automatic clean up. |
| 86 NotificationRegistrar registrar_; | 77 NotificationRegistrar registrar_; |
| 87 | 78 |
| 88 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceObserverBridge); | 79 DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceObserverBridge); |
| 89 }; | 80 }; |
| 90 | 81 |
| 91 @implementation BrowserActionsController | 82 @implementation BrowserActionsController |
| 92 | 83 |
| 93 - (id)initWithBrowser:(Browser*)browser | 84 - (id)initWithBrowser:(Browser*)browser |
| 94 containerView:(BrowserActionsContainerView*)container { | 85 containerView:(BrowserActionsContainerView*)container { |
| 95 DCHECK(browser && container); | 86 DCHECK(browser && container); |
| 96 | 87 |
| 97 if ((self = [super init])) { | 88 if ((self = [super init])) { |
| 98 browser_ = browser; | 89 browser_ = browser; |
| 99 profile_ = browser->profile(); | 90 profile_ = browser->profile(); |
| 91 observer_.reset(new ExtensionsServiceObserverBridge(self, profile_)); | |
| 92 ExtensionsService* extensionsService = profile_->GetExtensionsService(); | |
| 93 if (!extensionsService) { // |extensionsService| can be NULL in Incognito. | |
|
Erik does not do reviews
2010/02/10 16:57:41
FYI, this is going to change soon (see http://code
| |
| 94 toolbarModel_ = NULL; | |
| 95 } else { | |
| 96 toolbarModel_ = extensionsService->toolbar_model(); | |
| 97 toolbarModel_->AddObserver(observer_.get()); | |
| 98 } | |
| 100 | 99 |
| 101 containerView_ = container; | 100 containerView_ = container; |
| 102 [containerView_ setHidden:YES]; | 101 [containerView_ setHidden:YES]; |
| 103 observer_.reset(new ExtensionsServiceObserverBridge(self, profile_)); | 102 |
| 104 buttons_.reset([[NSMutableDictionary alloc] init]); | 103 buttons_.reset([[NSMutableDictionary alloc] init]); |
| 105 buttonOrder_.reset([[NSMutableArray alloc] init]); | 104 buttonOrder_.reset([[NSMutableArray alloc] init]); |
| 106 } | 105 } |
| 107 | 106 |
| 108 return self; | 107 return self; |
| 109 } | 108 } |
| 110 | 109 |
| 111 - (void)dealloc { | 110 - (void)dealloc { |
| 111 if (toolbarModel_) | |
| 112 toolbarModel_->RemoveObserver(observer_.get()); | |
| 113 | |
| 112 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 114 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 113 [super dealloc]; | 115 [super dealloc]; |
| 114 } | 116 } |
| 115 | 117 |
| 116 - (void)update { | 118 - (void)update { |
| 117 for (BrowserActionButton* button in [buttons_ allValues]) { | 119 for (BrowserActionButton* button in [buttons_ allValues]) { |
| 118 [button setTabId:[self currentTabId]]; | 120 [button setTabId:[self currentTabId]]; |
| 119 [button updateState]; | 121 [button updateState]; |
| 120 } | 122 } |
| 121 } | 123 } |
| 122 | 124 |
| 123 - (void)browserActionVisibilityHasChanged { | |
| 124 [containerView_ setNeedsDisplay:YES]; | |
| 125 } | |
| 126 | |
| 127 - (void)createButtons { | 125 - (void)createButtons { |
| 128 ExtensionsService* extensionsService = profile_->GetExtensionsService(); | 126 // No extensions in incognito mode. |
| 129 if (!extensionsService) // |extensionsService| can be NULL in Incognito. | 127 if (!toolbarModel_) |
| 130 return; | 128 return; |
| 131 | 129 |
| 132 for (size_t i = 0; i < extensionsService->extensions()->size(); ++i) { | 130 int i = 0; |
| 133 Extension* extension = extensionsService->GetExtensionById( | 131 for (ExtensionList::iterator iter = toolbarModel_->begin(); |
| 134 extensionsService->extensions()->at(i)->id(), false); | 132 iter != toolbarModel_->end(); ++iter) { |
| 135 if (extension->browser_action()) { | 133 [self createActionButtonForExtension:*iter withIndex:i++]; |
| 136 [self createActionButtonForExtension:extension]; | |
| 137 } | |
| 138 } | 134 } |
| 139 } | 135 } |
| 140 | 136 |
| 141 - (void)createActionButtonForExtension:(Extension*)extension { | 137 - (void)createActionButtonForExtension:(Extension*)extension |
| 138 withIndex:(int)index { | |
| 142 if (!extension->browser_action()) | 139 if (!extension->browser_action()) |
| 143 return; | 140 return; |
| 144 | 141 |
| 145 if ([buttons_ count] == 0) { | 142 // Show the container if it's the first button. Otherwise it will be shown |
| 146 // Only call if we're adding our first button, otherwise it will be shown | 143 // already. |
| 147 // already. | 144 if ([buttons_ count] == 0) |
| 148 [containerView_ setHidden:NO]; | 145 [containerView_ setHidden:NO]; |
| 149 } | |
| 150 | 146 |
| 151 int xOffset = | 147 BrowserActionButton* newButton = [[[BrowserActionButton alloc] |
| 152 [buttons_ count] * (kBrowserActionWidth + kBrowserActionButtonPadding); | 148 initWithExtension:extension |
| 153 BrowserActionButton* newButton = | 149 profile:profile_ |
| 154 [[[BrowserActionButton alloc] initWithExtension:extension | 150 tabId:[self currentTabId]] autorelease]; |
| 155 tabId:[self currentTabId] | |
| 156 xOffset:xOffset] autorelease]; | |
| 157 [newButton setTarget:self]; | 151 [newButton setTarget:self]; |
| 158 [newButton setAction:@selector(browserActionClicked:)]; | 152 [newButton setAction:@selector(browserActionClicked:)]; |
| 159 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); | 153 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); |
| 160 [buttons_ setObject:newButton forKey:buttonKey]; | 154 [buttons_ setObject:newButton forKey:buttonKey]; |
| 161 [buttonOrder_ addObject:newButton]; | 155 [buttonOrder_ insertObject:newButton atIndex:index]; |
| 162 [containerView_ addSubview:newButton]; | 156 [containerView_ addSubview:newButton]; |
| 157 [self repositionActionButtons]; | |
| 163 | 158 |
| 164 [[NSNotificationCenter defaultCenter] | 159 [[NSNotificationCenter defaultCenter] |
| 165 postNotificationName:kBrowserActionsChangedNotification object:self]; | 160 postNotificationName:kBrowserActionsChangedNotification object:self]; |
| 161 [containerView_ setNeedsDisplay:YES]; | |
|
Erik does not do reviews
2010/02/10 16:57:41
I thought adding and removing subviews always set
Bons
2010/02/10 17:52:30
I don't see anything in the docs, but also, we wan
| |
| 166 } | 162 } |
| 167 | 163 |
| 168 - (void)removeActionButtonForExtension:(Extension*)extension { | 164 - (void)removeActionButtonForExtension:(Extension*)extension { |
| 169 if (!extension->browser_action()) | 165 if (!extension->browser_action()) |
| 170 return; | 166 return; |
| 171 | 167 |
| 172 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); | 168 NSString* buttonKey = base::SysUTF8ToNSString(extension->id()); |
| 173 | 169 |
| 174 BrowserActionButton* button = [buttons_ objectForKey:buttonKey]; | 170 BrowserActionButton* button = [buttons_ objectForKey:buttonKey]; |
| 175 if (!button) { | 171 if (!button) { |
| 176 NOTREACHED(); | 172 NOTREACHED(); |
| 177 return; | 173 return; |
| 178 } | 174 } |
| 179 [button removeFromSuperview]; | 175 [button removeFromSuperview]; |
| 180 [buttons_ removeObjectForKey:buttonKey]; | 176 [buttons_ removeObjectForKey:buttonKey]; |
| 181 [buttonOrder_ removeObject:button]; | 177 [buttonOrder_ removeObject:button]; |
| 182 if ([buttons_ count] == 0) { | 178 if ([buttons_ count] == 0) { |
| 183 // No more buttons? Hide the container. | 179 // No more buttons? Hide the container. |
| 184 [containerView_ setHidden:YES]; | 180 [containerView_ setHidden:YES]; |
| 185 } else { | 181 } else { |
| 186 // repositionActionButtons only needs to be called if removing a browser | |
| 187 // action button because adding one will always append to the end of the | |
| 188 // container, while removing one may require that those to the right of it | |
| 189 // be shifted to the left. | |
| 190 [self repositionActionButtons]; | 182 [self repositionActionButtons]; |
| 191 } | 183 } |
| 192 [[NSNotificationCenter defaultCenter] | 184 [[NSNotificationCenter defaultCenter] |
| 193 postNotificationName:kBrowserActionsChangedNotification object:self]; | 185 postNotificationName:kBrowserActionsChangedNotification object:self]; |
| 186 [containerView_ setNeedsDisplay:YES]; | |
| 194 } | 187 } |
| 195 | 188 |
| 196 - (void)repositionActionButtons { | 189 - (void)repositionActionButtons { |
| 197 for (NSUInteger i = 0; i < [buttonOrder_ count]; ++i) { | 190 for (NSUInteger i = 0; i < [buttonOrder_ count]; ++i) { |
| 198 CGFloat xOffset = i * (kBrowserActionWidth + kBrowserActionButtonPadding); | 191 CGFloat xOffset = i * (kBrowserActionWidth + kBrowserActionButtonPadding); |
| 199 BrowserActionButton* button = [buttonOrder_ objectAtIndex:i]; | 192 BrowserActionButton* button = [buttonOrder_ objectAtIndex:i]; |
| 200 NSRect buttonFrame = [button frame]; | 193 NSRect buttonFrame = [button frame]; |
| 201 buttonFrame.origin.x = xOffset; | 194 buttonFrame.origin.x = xOffset; |
| 202 [button setFrame:buttonFrame]; | 195 [button setFrame:buttonFrame]; |
| 203 } | 196 } |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 } | 260 } |
| 268 NOTREACHED(); | 261 NOTREACHED(); |
| 269 return nil; | 262 return nil; |
| 270 } | 263 } |
| 271 | 264 |
| 272 - (NSButton*)buttonWithIndex:(int)index { | 265 - (NSButton*)buttonWithIndex:(int)index { |
| 273 return [buttonOrder_ objectAtIndex:(NSUInteger)index]; | 266 return [buttonOrder_ objectAtIndex:(NSUInteger)index]; |
| 274 } | 267 } |
| 275 | 268 |
| 276 @end | 269 @end |
| OLD | NEW |