summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/cocoa/extensions/extension_install_prompt_controller.mm
blob: e40fcd4a0441deceffa0aa8d40fa38a65d582c4a (plain)
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
// 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/ui/cocoa/extensions/extension_install_prompt_controller.h"

#include "app/l10n_util.h"
#include "app/l10n_util_mac.h"
#include "base/mac_util.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/extensions/extension.h"
#include "grit/generated_resources.h"
#include "skia/ext/skia_utils_mac.h"

namespace {

// Maximum height we will adjust controls to when trying to accomodate their
// contents.
const CGFloat kMaxControlHeight = 400;

// Adjust a control's height so that its content its not clipped. Returns the
// amount the control's height had to be adjusted.
CGFloat AdjustControlHeightToFitContent(NSControl* control) {
  NSRect currentRect = [control frame];
  NSRect fitRect = currentRect;
  fitRect.size.height = kMaxControlHeight;
  CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height;
  CGFloat offset = desiredHeight - currentRect.size.height;

  [control setFrameSize:NSMakeSize(currentRect.size.width,
                                   currentRect.size.height + offset)];
  return offset;
}

// Moves the control vertically by the specified amount.
void OffsetControlVertically(NSControl* control, CGFloat amount) {
  NSPoint origin = [control frame].origin;
  origin.y += amount;
  [control setFrameOrigin:origin];
}

}

@implementation ExtensionInstallPromptController

@synthesize iconView = iconView_;
@synthesize titleField = titleField_;
@synthesize subtitleField = subtitleField_;
@synthesize warningsField = warningsField_;
@synthesize warningsBox= warningsBox_;
@synthesize cancelButton = cancelButton_;
@synthesize okButton = okButton_;

- (id)initWithParentWindow:(NSWindow*)window
                   profile:(Profile*)profile
                 extension:(const Extension*)extension
                  delegate:(ExtensionInstallUI::Delegate*)delegate
                      icon:(SkBitmap*)icon
                  warnings:(const std::vector<string16>&)warnings {
  NSString* nibpath = nil;

  // We use a different XIB in the case of no warnings, that is a little bit
  // more nicely laid out.
  if (warnings.empty()) {
    nibpath = [mac_util::MainAppBundle()
               pathForResource:@"ExtensionInstallPromptNoWarnings"
                        ofType:@"nib"];
  } else {
   nibpath = [mac_util::MainAppBundle()
              pathForResource:@"ExtensionInstallPrompt"
                       ofType:@"nib"];
  }

  if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
    parentWindow_ = window;
    profile_ = profile;
    icon_ = *icon;
    delegate_ = delegate;

    title_.reset(
        [l10n_util::GetNSStringF(IDS_EXTENSION_INSTALL_PROMPT_HEADING,
                                 UTF8ToUTF16(extension->name())) retain]);

    // We display the warnings as a simple text string, separated by newlines.
    if (!warnings.empty()) {
      string16 joined_warnings;
      for (size_t i = 0; i < warnings.size(); ++i) {
        if (i > 0)
          joined_warnings += UTF8ToUTF16("\n\n");

        joined_warnings += warnings[i];
      }

      warnings_.reset(
          [base::SysUTF16ToNSString(joined_warnings) retain]);
    }
  }
  return self;
}

- (void)runAsModalSheet {
  [NSApp beginSheet:[self window]
     modalForWindow:parentWindow_
      modalDelegate:self
     didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
        contextInfo:nil];
}

- (IBAction)cancel:(id)sender {
  delegate_->InstallUIAbort();
  [NSApp endSheet:[self window]];
}

- (IBAction)ok:(id)sender {
  delegate_->InstallUIProceed();
  [NSApp endSheet:[self window]];
}

- (void)awakeFromNib {
  [titleField_ setStringValue:title_.get()];

  NSImage* image = gfx::SkBitmapToNSImage(icon_);
  [iconView_ setImage:image];

  // Make sure we're the window's delegate as set in the nib.
  DCHECK_EQ(self, static_cast<ExtensionInstallPromptController*>(
                      [[self window] delegate]));

  // If there are any warnings, then we have to do some special layout.
  if ([warnings_.get() length] > 0) {
    [warningsField_ setStringValue:warnings_.get()];

    // The dialog is laid out in the NIB exactly how we want it assuming that
    // each label fits on one line. However, for each label, we want to allow
    // wrapping onto multiple lines. So we accumulate an offset by measuring how
    // big each label wants to be, and comparing it to how bit it actually is.
    // Then we shift each label down and resize by the appropriate amount, then
    // finally resize the window.
    CGFloat totalOffset = 0.0;

    // Text fields.
    totalOffset += AdjustControlHeightToFitContent(titleField_);
    OffsetControlVertically(titleField_, -totalOffset);

    totalOffset += AdjustControlHeightToFitContent(subtitleField_);
    OffsetControlVertically(subtitleField_, -totalOffset);

    CGFloat warningsOffset = AdjustControlHeightToFitContent(warningsField_);
    OffsetControlVertically(warningsField_, -warningsOffset);
    totalOffset += warningsOffset;

    NSRect warningsBoxRect = [warningsBox_ frame];
    warningsBoxRect.origin.y -= totalOffset;
    warningsBoxRect.size.height += warningsOffset;
    [warningsBox_ setFrame:warningsBoxRect];

    // buttons are positioned automatically in the XIB.

    // Finally, adjust the window size.
    NSRect currentRect = [[self window] frame];
    [[self window] setFrame:NSMakeRect(currentRect.origin.x,
                                       currentRect.origin.y - totalOffset,
                                       currentRect.size.width,
                                       currentRect.size.height + totalOffset)
                    display:NO];
  }
}

- (void)didEndSheet:(NSWindow*)sheet
         returnCode:(int)returnCode
        contextInfo:(void*)contextInfo {
  [sheet close];
}

- (void)windowWillClose:(NSNotification*)notification {
  [self autorelease];
}

@end  // ExtensionInstallPromptController


void ExtensionInstallUI::ShowExtensionInstallUIPrompt2Impl(
    Profile* profile,
    Delegate* delegate,
    const Extension* extension,
    SkBitmap* icon,
    const std::vector<string16>& warnings) {
  Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
  if (!browser) {
    delegate->InstallUIAbort();
    return;
  }

  BrowserWindow* window = browser->window();
  if (!window) {
    delegate->InstallUIAbort();
    return;
  }

  gfx::NativeWindow native_window = window->GetNativeHandle();

  ExtensionInstallPromptController* controller =
      [[ExtensionInstallPromptController alloc]
        initWithParentWindow:native_window
                     profile:profile
                   extension:extension
                    delegate:delegate
                        icon:icon
                   warnings:warnings];

  [controller runAsModalSheet];
}