Index: chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm |
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm |
index 888b58cf7c63adcc10cfcedb9a20bdc023487dfd..34f6227f409557f2fd821e7ba2a88ab20c35eeca 100644 |
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm |
+++ b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm |
@@ -9,6 +9,7 @@ |
#include "base/mac/mac_util.h" |
#include "base/sys_string_conversions.h" |
#include "base/utf_string_conversions.h" |
+#include "chrome/browser/extensions/bundle_installer.h" |
#include "chrome/browser/ui/browser.h" |
#include "chrome/browser/ui/browser_window.h" |
#include "chrome/browser/ui/cocoa/browser_window_cocoa.h" |
@@ -31,6 +32,7 @@ |
#include "ui/base/l10n/l10n_util.h" |
using content::BrowserThread; |
+using extensions::BundleInstaller; |
// C++ class that receives EXTENSION_LOADED notifications and proxies them back |
// to |controller|. |
@@ -80,26 +82,31 @@ class ExtensionLoadedNotificationObserver |
@implementation ExtensionInstalledBubbleController |
@synthesize extension = extension_; |
+@synthesize bundle = bundle_; |
@synthesize pageActionRemoved = pageActionRemoved_; // Exposed for unit test. |
- (id)initWithParentWindow:(NSWindow*)parentWindow |
extension:(const Extension*)extension |
+ bundle:(const BundleInstaller*)bundle |
browser:(Browser*)browser |
icon:(SkBitmap)icon { |
- NSString* nibPath = |
- [base::mac::FrameworkBundle() pathForResource:@"ExtensionInstalledBubble" |
- ofType:@"nib"]; |
+ NSString* nibName = bundle ? @"ExtensionInstalledBubbleBundle" : |
+ @"ExtensionInstalledBubble"; |
+ NSString* nibPath = [base::mac::FrameworkBundle() pathForResource:nibName |
+ ofType:@"nib"]; |
if ((self = [super initWithWindowNibPath:nibPath owner:self])) { |
DCHECK(parentWindow); |
parentWindow_ = parentWindow; |
- DCHECK(extension); |
extension_ = extension; |
+ bundle_ = bundle; |
DCHECK(browser); |
browser_ = browser; |
icon_.reset([gfx::SkBitmapToNSImage(icon) retain]); |
pageActionRemoved_ = NO; |
- if (!extension->omnibox_keyword().empty()) { |
+ if (bundle_) { |
+ type_ = extension_installed_bubble::kBundle; |
+ } else if (!extension->omnibox_keyword().empty()) { |
type_ = extension_installed_bubble::kOmniboxKeyword; |
} else if (extension->browser_action()) { |
type_ = extension_installed_bubble::kBrowserAction; |
@@ -110,9 +117,13 @@ class ExtensionLoadedNotificationObserver |
NOTREACHED(); // kGeneric installs handled in the extension_install_ui. |
} |
- // Start showing window only after extension has fully loaded. |
- extensionObserver_.reset(new ExtensionLoadedNotificationObserver( |
- self, browser->profile())); |
+ if (type_ == extension_installed_bubble::kBundle) { |
+ [self showWindow:self]; |
+ } else { |
+ // Start showing window only after extension has fully loaded. |
+ extensionObserver_.reset(new ExtensionLoadedNotificationObserver( |
+ self, browser->profile())); |
+ } |
// Watch to see if the parent window closes, and if so, close this one. |
NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
@@ -224,6 +235,14 @@ class ExtensionLoadedNotificationObserver |
locationBarView->GetPageActionBubblePoint(extension_->page_action()); |
break; |
} |
+ case extension_installed_bubble::kBundle: { |
+ NSView* wrenchButton = |
+ [[window->cocoa_controller() toolbarController] wrenchButton]; |
+ const NSRect bounds = [wrenchButton bounds]; |
+ NSPoint anchor = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); |
+ arrowPoint = [wrenchButton convertPoint:anchor toView:nil]; |
+ break; |
+ } |
default: { |
NOTREACHED() << "Generic extension type not allowed in install bubble."; |
} |
@@ -284,6 +303,9 @@ class ExtensionLoadedNotificationObserver |
[infoBubbleView_ setArrowLocation:info_bubble::kTopRight]; |
} |
+ if (type_ == extension_installed_bubble::kBundle) |
+ return window; |
+ |
// Set appropriate icon, resizing if necessary. |
if ([icon_ size].width > extension_installed_bubble::kIconSize) { |
[icon_ setSize:NSMakeSize(extension_installed_bubble::kIconSize, |
@@ -292,7 +314,7 @@ class ExtensionLoadedNotificationObserver |
[iconImage_ setImage:icon_]; |
[iconImage_ setNeedsDisplay:YES]; |
return window; |
- } |
+} |
// Calculate the height of each install message, resizing messages in their |
// frames to fit window width. Return the new window height, based on the |
@@ -303,14 +325,16 @@ class ExtensionLoadedNotificationObserver |
int newWindowHeight = 2 * extension_installed_bubble::kOuterVerticalMargin; |
// First part of extension installed message. |
- string16 extension_name = UTF8ToUTF16(extension_->name().c_str()); |
- base::i18n::AdjustStringForLocaleDirection(&extension_name); |
- [extensionInstalledMsg_ setStringValue:l10n_util::GetNSStringF( |
- IDS_EXTENSION_INSTALLED_HEADING, extension_name)]; |
- [GTMUILocalizerAndLayoutTweaker |
+ if (type_ != extension_installed_bubble::kBundle) { |
+ string16 extension_name = UTF8ToUTF16(extension_->name().c_str()); |
+ base::i18n::AdjustStringForLocaleDirection(&extension_name); |
+ [extensionInstalledMsg_ setStringValue:l10n_util::GetNSStringF( |
+ IDS_EXTENSION_INSTALLED_HEADING, extension_name)]; |
+ [GTMUILocalizerAndLayoutTweaker |
sizeToFitFixedWidthTextField:extensionInstalledMsg_]; |
- newWindowHeight += [extensionInstalledMsg_ frame].size.height + |
- extension_installed_bubble::kInnerVerticalMargin; |
+ newWindowHeight += [extensionInstalledMsg_ frame].size.height + |
+ extension_installed_bubble::kInnerVerticalMargin; |
+ } |
// If type is page action, include a special message about page actions. |
if (type_ == extension_installed_bubble::kPageAction) { |
@@ -337,19 +361,100 @@ class ExtensionLoadedNotificationObserver |
extension_installed_bubble::kInnerVerticalMargin; |
} |
- // Second part of extension installed message. |
- [[extensionInstalledInfoMsg_ cell] |
- setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; |
- [GTMUILocalizerAndLayoutTweaker |
- sizeToFitFixedWidthTextField:extensionInstalledInfoMsg_]; |
- newWindowHeight += [extensionInstalledInfoMsg_ frame].size.height; |
+ // If type is bundle, list the extensions that were installed and those that |
+ // failed. |
+ if (type_ == extension_installed_bubble::kBundle) { |
+ NSInteger installedListHeight = |
+ [self addExtensionList:installedHeadingMsg_ |
+ itemsMsg:installedItemsMsg_ |
+ state:BundleInstaller::Item::STATE_INSTALLED]; |
+ |
+ NSInteger failedListHeight = |
+ [self addExtensionList:failedHeadingMsg_ |
+ itemsMsg:failedItemsMsg_ |
+ state:BundleInstaller::Item::STATE_FAILED]; |
+ |
+ newWindowHeight += installedListHeight + failedListHeight; |
+ |
+ // Put some space between the lists if both are present. |
+ if (installedListHeight > 0 && failedListHeight > 0) |
+ newWindowHeight += extension_installed_bubble::kInnerVerticalMargin; |
+ } else { |
+ // Second part of extension installed message. |
+ [[extensionInstalledInfoMsg_ cell] |
+ setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; |
+ [GTMUILocalizerAndLayoutTweaker |
+ sizeToFitFixedWidthTextField:extensionInstalledInfoMsg_]; |
+ newWindowHeight += [extensionInstalledInfoMsg_ frame].size.height; |
+ } |
return newWindowHeight; |
} |
+- (NSInteger)addExtensionList:(NSTextField*)headingMsg |
+ itemsMsg:(NSTextField*)itemsMsg |
+ state:(BundleInstaller::Item::State)state { |
+ string16 heading = bundle_->GetHeadingTextFor(state); |
+ bool hidden = heading.empty(); |
+ [headingMsg setHidden:hidden]; |
+ [itemsMsg setHidden:hidden]; |
+ if (hidden) |
+ return 0; |
+ |
+ [headingMsg setStringValue:base::SysUTF16ToNSString(heading)]; |
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:headingMsg]; |
+ |
+ NSMutableString* joinedItems = [NSMutableString string]; |
+ BundleInstaller::ItemList items = bundle_->GetItemsWithState(state); |
+ for (size_t i = 0; i < items.size(); ++i) { |
+ if (i > 0) |
+ [joinedItems appendString:@"\n"]; |
+ [joinedItems appendString:base::SysUTF16ToNSString( |
+ items[i].GetNameForDisplay())]; |
+ } |
+ |
+ [itemsMsg setStringValue:joinedItems]; |
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:itemsMsg]; |
+ |
+ return [headingMsg frame].size.height + |
+ extension_installed_bubble::kInnerVerticalMargin + |
+ [itemsMsg frame].size.height; |
+} |
+ |
// Adjust y-position of messages to sit properly in new window height. |
- (void)setMessageFrames:(int)newWindowHeight { |
- // The extension messages will always be shown. |
+ if (type_ == extension_installed_bubble::kBundle) { |
+ // Layout the messages from the bottom up. |
+ NSTextField* msgs[] = { failedItemsMsg_, failedHeadingMsg_, |
+ installedItemsMsg_, installedHeadingMsg_ }; |
+ NSInteger offsetFromBottom = 0; |
+ BOOL isFirstVisible = YES; |
+ for (size_t i = 0; i < arraysize(msgs); ++i) { |
+ if ([msgs[i] isHidden]) |
+ continue; |
+ |
+ NSRect frame = [msgs[i] frame]; |
+ NSInteger margin = isFirstVisible ? |
+ extension_installed_bubble::kOuterVerticalMargin : |
+ extension_installed_bubble::kInnerVerticalMargin; |
+ |
+ frame.origin.y = offsetFromBottom + margin; |
+ [msgs[i] setFrame:frame]; |
+ offsetFromBottom += frame.size.height + margin; |
+ |
+ isFirstVisible = NO; |
+ } |
+ |
+ // Move the close button a bit to vertically align it with the heading. |
+ NSInteger closeButtonFudge = 1; |
+ NSRect frame = [closeButton_ frame]; |
+ frame.origin.y = newWindowHeight - (frame.size.height + closeButtonFudge + |
+ extension_installed_bubble::kOuterVerticalMargin); |
+ [closeButton_ setFrame:frame]; |
+ |
+ return; |
+ } |
+ |
NSRect extensionMessageFrame1 = [extensionInstalledMsg_ frame]; |
NSRect extensionMessageFrame2 = [extensionInstalledInfoMsg_ frame]; |