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

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

Issue 6269009: Move loose extension files in c/b/ui/cocoa/ into the extensions/ subdir... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 11 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #import "extension_installed_bubble_controller.h"
6
7 #include "app/l10n_util.h"
8 #include "base/i18n/rtl.h"
9 #include "base/mac/mac_util.h"
10 #include "base/sys_string_conversions.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
13 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
15 #include "chrome/browser/ui/cocoa/hover_close_button.h"
16 #include "chrome/browser/ui/cocoa/info_bubble_view.h"
17 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
18 #include "chrome/browser/ui/cocoa/toolbar_controller.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/common/extensions/extension.h"
22 #include "chrome/common/extensions/extension_action.h"
23 #include "chrome/common/notification_details.h"
24 #include "chrome/common/notification_registrar.h"
25 #include "chrome/common/notification_source.h"
26 #include "grit/generated_resources.h"
27 #import "skia/ext/skia_utils_mac.h"
28 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
29
30
31 // C++ class that receives EXTENSION_LOADED notifications and proxies them back
32 // to |controller|.
33 class ExtensionLoadedNotificationObserver : public NotificationObserver {
34 public:
35 ExtensionLoadedNotificationObserver(
36 ExtensionInstalledBubbleController* controller, Profile* profile)
37 : controller_(controller) {
38 registrar_.Add(this, NotificationType::EXTENSION_LOADED,
39 Source<Profile>(profile));
40 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
41 Source<Profile>(profile));
42 }
43
44 private:
45 // NotificationObserver implementation. Tells the controller to start showing
46 // its window on the main thread when the extension has finished loading.
47 void Observe(NotificationType type,
48 const NotificationSource& source,
49 const NotificationDetails& details) {
50 if (type == NotificationType::EXTENSION_LOADED) {
51 const Extension* extension = Details<const Extension>(details).ptr();
52 if (extension == [controller_ extension]) {
53 [controller_ performSelectorOnMainThread:@selector(showWindow:)
54 withObject:controller_
55 waitUntilDone:NO];
56 }
57 } else if (type == NotificationType::EXTENSION_UNLOADED) {
58 const Extension* extension = Details<const Extension>(details).ptr();
59 if (extension == [controller_ extension]) {
60 [controller_ performSelectorOnMainThread:@selector(extensionUnloaded:)
61 withObject:controller_
62 waitUntilDone:NO];
63 }
64 } else {
65 NOTREACHED() << "Received unexpected notification.";
66 }
67 }
68
69 NotificationRegistrar registrar_;
70 ExtensionInstalledBubbleController* controller_; // weak, owns us
71 };
72
73 @implementation ExtensionInstalledBubbleController
74
75 @synthesize extension = extension_;
76 @synthesize pageActionRemoved = pageActionRemoved_; // Exposed for unit test.
77
78 - (id)initWithParentWindow:(NSWindow*)parentWindow
79 extension:(const Extension*)extension
80 browser:(Browser*)browser
81 icon:(SkBitmap)icon {
82 NSString* nibPath =
83 [base::mac::MainAppBundle() pathForResource:@"ExtensionInstalledBubble"
84 ofType:@"nib"];
85 if ((self = [super initWithWindowNibPath:nibPath owner:self])) {
86 DCHECK(parentWindow);
87 parentWindow_ = parentWindow;
88 DCHECK(extension);
89 extension_ = extension;
90 DCHECK(browser);
91 browser_ = browser;
92 icon_.reset([gfx::SkBitmapToNSImage(icon) retain]);
93 pageActionRemoved_ = NO;
94
95 if (!extension->omnibox_keyword().empty()) {
96 type_ = extension_installed_bubble::kOmniboxKeyword;
97 } else if (extension->browser_action()) {
98 type_ = extension_installed_bubble::kBrowserAction;
99 } else if (extension->page_action() &&
100 !extension->page_action()->default_icon_path().empty()) {
101 type_ = extension_installed_bubble::kPageAction;
102 } else {
103 NOTREACHED(); // kGeneric installs handled in the extension_install_ui.
104 }
105
106 // Start showing window only after extension has fully loaded.
107 extensionObserver_.reset(new ExtensionLoadedNotificationObserver(
108 self, browser->profile()));
109 }
110 return self;
111 }
112
113 - (void)dealloc {
114 [[NSNotificationCenter defaultCenter] removeObserver:self];
115 [super dealloc];
116 }
117
118 - (void)close {
119 [parentWindow_ removeChildWindow:[self window]];
120 [super close];
121 }
122
123 - (void)windowWillClose:(NSNotification*)notification {
124 // Turn off page action icon preview when the window closes, unless we
125 // already removed it when the window resigned key status.
126 [self removePageActionPreviewIfNecessary];
127 extension_ = NULL;
128 browser_ = NULL;
129 parentWindow_ = nil;
130 // We caught a close so we don't need to watch for the parent closing.
131 [[NSNotificationCenter defaultCenter] removeObserver:self];
132 [self autorelease];
133 }
134
135 // The controller is the delegate of the window, so it receives "did resign
136 // key" notifications. When key is resigned, close the window.
137 - (void)windowDidResignKey:(NSNotification*)notification {
138 NSWindow* window = [self window];
139 DCHECK_EQ([notification object], window);
140 DCHECK([window isVisible]);
141
142 // If the browser window is closing, we need to remove the page action
143 // immediately, otherwise the closing animation may overlap with
144 // browser destruction.
145 [self removePageActionPreviewIfNecessary];
146 [self close];
147 }
148
149 - (IBAction)closeWindow:(id)sender {
150 DCHECK([[self window] isVisible]);
151 [self close];
152 }
153
154 // Extracted to a function here so that it can be overwritten for unit
155 // testing.
156 - (void)removePageActionPreviewIfNecessary {
157 if (!extension_ || !extension_->page_action() || pageActionRemoved_)
158 return;
159 pageActionRemoved_ = YES;
160
161 BrowserWindowCocoa* window =
162 static_cast<BrowserWindowCocoa*>(browser_->window());
163 LocationBarViewMac* locationBarView =
164 [window->cocoa_controller() locationBarBridge];
165 locationBarView->SetPreviewEnabledPageAction(extension_->page_action(),
166 false); // disables preview.
167 }
168
169 // The extension installed bubble points at the browser action icon or the
170 // page action icon (shown as a preview), depending on the extension type.
171 // We need to calculate the location of these icons and the size of the
172 // message itself (which varies with the title of the extension) in order
173 // to figure out the origin point for the extension installed bubble.
174 // TODO(mirandac): add framework to easily test extension UI components!
175 - (NSPoint)calculateArrowPoint {
176 BrowserWindowCocoa* window =
177 static_cast<BrowserWindowCocoa*>(browser_->window());
178 NSPoint arrowPoint = NSZeroPoint;
179
180 switch(type_) {
181 case extension_installed_bubble::kOmniboxKeyword: {
182 LocationBarViewMac* locationBarView =
183 [window->cocoa_controller() locationBarBridge];
184 arrowPoint = locationBarView->GetPageInfoBubblePoint();
185 break;
186 }
187 case extension_installed_bubble::kBrowserAction: {
188 BrowserActionsController* controller =
189 [[window->cocoa_controller() toolbarController]
190 browserActionsController];
191 arrowPoint = [controller popupPointForBrowserAction:extension_];
192 break;
193 }
194 case extension_installed_bubble::kPageAction: {
195 LocationBarViewMac* locationBarView =
196 [window->cocoa_controller() locationBarBridge];
197
198 // Tell the location bar to show a preview of the page action icon, which
199 // would ordinarily only be displayed on a page of the appropriate type.
200 // We remove this preview when the extension installed bubble closes.
201 locationBarView->SetPreviewEnabledPageAction(extension_->page_action(),
202 true);
203
204 // Find the center of the bottom of the page action icon.
205 arrowPoint =
206 locationBarView->GetPageActionBubblePoint(extension_->page_action());
207 break;
208 }
209 default: {
210 NOTREACHED() << "Generic extension type not allowed in install bubble.";
211 }
212 }
213 return arrowPoint;
214 }
215
216 // We want this to be a child of a browser window. addChildWindow:
217 // (called from this function) will bring the window on-screen;
218 // unfortunately, [NSWindowController showWindow:] will also bring it
219 // on-screen (but will cause unexpected changes to the window's
220 // position). We cannot have an addChildWindow: and a subsequent
221 // showWindow:. Thus, we have our own version.
222 - (void)showWindow:(id)sender {
223 // Generic extensions get an infobar rather than a bubble.
224 DCHECK(type_ != extension_installed_bubble::kGeneric);
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
226
227 // Load nib and calculate height based on messages to be shown.
228 NSWindow* window = [self initializeWindow];
229 int newWindowHeight = [self calculateWindowHeight];
230 [infoBubbleView_ setFrameSize:NSMakeSize(
231 NSWidth([[window contentView] bounds]), newWindowHeight)];
232 NSSize windowDelta = NSMakeSize(
233 0, newWindowHeight - NSHeight([[window contentView] bounds]));
234 windowDelta = [[window contentView] convertSize:windowDelta toView:nil];
235 NSRect newFrame = [window frame];
236 newFrame.size.height += windowDelta.height;
237 [window setFrame:newFrame display:NO];
238
239 // Now that we have resized the window, adjust y pos of the messages.
240 [self setMessageFrames:newWindowHeight];
241
242 // Find window origin, taking into account bubble size and arrow location.
243 NSPoint origin =
244 [parentWindow_ convertBaseToScreen:[self calculateArrowPoint]];
245 NSSize offsets = NSMakeSize(info_bubble::kBubbleArrowXOffset +
246 info_bubble::kBubbleArrowWidth / 2.0, 0);
247 offsets = [[window contentView] convertSize:offsets toView:nil];
248 if ([infoBubbleView_ arrowLocation] == info_bubble::kTopRight)
249 origin.x -= NSWidth([window frame]) - offsets.width;
250 origin.y -= NSHeight([window frame]);
251 [window setFrameOrigin:origin];
252
253 [parentWindow_ addChildWindow:window
254 ordered:NSWindowAbove];
255 [window makeKeyAndOrderFront:self];
256 }
257
258 // Finish nib loading, set arrow location and load icon into window. This
259 // function is exposed for unit testing.
260 - (NSWindow*)initializeWindow {
261 NSWindow* window = [self window]; // completes nib load
262
263 if (type_ == extension_installed_bubble::kOmniboxKeyword) {
264 [infoBubbleView_ setArrowLocation:info_bubble::kTopLeft];
265 } else {
266 [infoBubbleView_ setArrowLocation:info_bubble::kTopRight];
267 }
268
269 // Set appropriate icon, resizing if necessary.
270 if ([icon_ size].width > extension_installed_bubble::kIconSize) {
271 [icon_ setSize:NSMakeSize(extension_installed_bubble::kIconSize,
272 extension_installed_bubble::kIconSize)];
273 }
274 [iconImage_ setImage:icon_];
275 [iconImage_ setNeedsDisplay:YES];
276 return window;
277 }
278
279 // Calculate the height of each install message, resizing messages in their
280 // frames to fit window width. Return the new window height, based on the
281 // total of all message heights.
282 - (int)calculateWindowHeight {
283 // Adjust the window height to reflect the sum height of all messages
284 // and vertical padding.
285 int newWindowHeight = 2 * extension_installed_bubble::kOuterVerticalMargin;
286
287 // First part of extension installed message.
288 string16 extension_name = UTF8ToUTF16(extension_->name().c_str());
289 base::i18n::AdjustStringForLocaleDirection(&extension_name);
290 [extensionInstalledMsg_ setStringValue:l10n_util::GetNSStringF(
291 IDS_EXTENSION_INSTALLED_HEADING, extension_name)];
292 [GTMUILocalizerAndLayoutTweaker
293 sizeToFitFixedWidthTextField:extensionInstalledMsg_];
294 newWindowHeight += [extensionInstalledMsg_ frame].size.height +
295 extension_installed_bubble::kInnerVerticalMargin;
296
297 // If type is page action, include a special message about page actions.
298 if (type_ == extension_installed_bubble::kPageAction) {
299 [extraInfoMsg_ setHidden:NO];
300 [[extraInfoMsg_ cell]
301 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
302 [GTMUILocalizerAndLayoutTweaker
303 sizeToFitFixedWidthTextField:extraInfoMsg_];
304 newWindowHeight += [extraInfoMsg_ frame].size.height +
305 extension_installed_bubble::kInnerVerticalMargin;
306 }
307
308 // If type is omnibox keyword, include a special message about the keyword.
309 if (type_ == extension_installed_bubble::kOmniboxKeyword) {
310 [extraInfoMsg_ setStringValue:l10n_util::GetNSStringF(
311 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO,
312 UTF8ToUTF16(extension_->omnibox_keyword()))];
313 [extraInfoMsg_ setHidden:NO];
314 [[extraInfoMsg_ cell]
315 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
316 [GTMUILocalizerAndLayoutTweaker
317 sizeToFitFixedWidthTextField:extraInfoMsg_];
318 newWindowHeight += [extraInfoMsg_ frame].size.height +
319 extension_installed_bubble::kInnerVerticalMargin;
320 }
321
322 // Second part of extension installed message.
323 [[extensionInstalledInfoMsg_ cell]
324 setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
325 [GTMUILocalizerAndLayoutTweaker
326 sizeToFitFixedWidthTextField:extensionInstalledInfoMsg_];
327 newWindowHeight += [extensionInstalledInfoMsg_ frame].size.height;
328
329 return newWindowHeight;
330 }
331
332 // Adjust y-position of messages to sit properly in new window height.
333 - (void)setMessageFrames:(int)newWindowHeight {
334 // The extension messages will always be shown.
335 NSRect extensionMessageFrame1 = [extensionInstalledMsg_ frame];
336 NSRect extensionMessageFrame2 = [extensionInstalledInfoMsg_ frame];
337
338 extensionMessageFrame1.origin.y = newWindowHeight - (
339 extensionMessageFrame1.size.height +
340 extension_installed_bubble::kOuterVerticalMargin);
341 [extensionInstalledMsg_ setFrame:extensionMessageFrame1];
342 if (type_ == extension_installed_bubble::kPageAction ||
343 type_ == extension_installed_bubble::kOmniboxKeyword) {
344 // The extra message is only shown when appropriate.
345 NSRect extraMessageFrame = [extraInfoMsg_ frame];
346 extraMessageFrame.origin.y = extensionMessageFrame1.origin.y - (
347 extraMessageFrame.size.height +
348 extension_installed_bubble::kInnerVerticalMargin);
349 [extraInfoMsg_ setFrame:extraMessageFrame];
350 extensionMessageFrame2.origin.y = extraMessageFrame.origin.y - (
351 extensionMessageFrame2.size.height +
352 extension_installed_bubble::kInnerVerticalMargin);
353 } else {
354 extensionMessageFrame2.origin.y = extensionMessageFrame1.origin.y - (
355 extensionMessageFrame2.size.height +
356 extension_installed_bubble::kInnerVerticalMargin);
357 }
358 [extensionInstalledInfoMsg_ setFrame:extensionMessageFrame2];
359 }
360
361 // Exposed for unit testing.
362 - (NSRect)getExtensionInstalledMsgFrame {
363 return [extensionInstalledMsg_ frame];
364 }
365
366 - (NSRect)getExtraInfoMsgFrame {
367 return [extraInfoMsg_ frame];
368 }
369
370 - (NSRect)getExtensionInstalledInfoMsgFrame {
371 return [extensionInstalledInfoMsg_ frame];
372 }
373
374 - (void)extensionUnloaded:(id)sender {
375 extension_ = NULL;
376 }
377
378 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698